Browse Source

feat: unified user group notification

Cat 1 year ago
parent
commit
d8d4abbb15

+ 2 - 1
composer.json

@@ -17,6 +17,7 @@
         "alibabacloud/dm-20170622": "^1",
         "alipaysdk/openapi": "*@dev",
         "aws/aws-sdk-php": "^3",
+        "danielsreichenbach/geoip2-update": "^2",
         "geoip2/geoip2": "^3",
         "guzzlehttp/guzzle": "^7",
         "guzzlehttp/psr7": "^2",
@@ -24,6 +25,7 @@
         "illuminate/pagination": "^11",
         "irazasyed/telegram-bot-sdk": "^3",
         "lcobucci/jwt": "^5",
+        "league/html-to-markdown": "^5",
         "mailchimp/transactional": "^1",
         "mailgun/mailgun-php": "^4",
         "monolog/monolog": "^3",
@@ -41,7 +43,6 @@
         "stripe/stripe-php": "^14",
         "symfony/http-client": "^7",
         "symfony/translation": "^7",
-        "danielsreichenbach/geoip2-update": "^2",
         "twig/twig": "^3",
         "vectorface/googleauthenticator": "^3",
         "voku/anti-xss": "^4"

+ 116 - 27
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "7ceb8d46d3d7e4d034d6db7cf885e1a7",
+    "content-hash": "80e2b1dd8b3a95458b9e77a57cf55b1f",
     "packages": [
         {
             "name": "adbario/php-dot-notation",
@@ -622,16 +622,16 @@
         },
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.306.2",
+            "version": "3.306.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "0de11adba8f174a76346c9dbb175c2030ff5c8f2"
+                "reference": "f50ff987dbe6d3e60d68794970422eae71ffb525"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0de11adba8f174a76346c9dbb175c2030ff5c8f2",
-                "reference": "0de11adba8f174a76346c9dbb175c2030ff5c8f2",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/f50ff987dbe6d3e60d68794970422eae71ffb525",
+                "reference": "f50ff987dbe6d3e60d68794970422eae71ffb525",
                 "shasum": ""
             },
             "require": {
@@ -711,9 +711,9 @@
             "support": {
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.306.2"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.306.4"
             },
-            "time": "2024-05-08T18:07:54+00:00"
+            "time": "2024-05-10T18:23:57+00:00"
         },
         {
             "name": "bacon/bacon-qr-code",
@@ -2473,6 +2473,95 @@
             },
             "time": "2022-10-29T09:31:25+00:00"
         },
+        {
+            "name": "league/html-to-markdown",
+            "version": "5.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/html-to-markdown.git",
+                "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0b4066eede55c48f38bcee4fb8f0aa85654390fd",
+                "reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-xml": "*",
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "mikehaertl/php-shellcommand": "^1.1.0",
+                "phpstan/phpstan": "^1.8.8",
+                "phpunit/phpunit": "^8.5 || ^9.2",
+                "scrutinizer/ocular": "^1.6",
+                "unleashedtech/php-coding-standard": "^2.7 || ^3.0",
+                "vimeo/psalm": "^4.22 || ^5.0"
+            },
+            "bin": [
+                "bin/html-to-markdown"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.2-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\HTMLToMarkdown\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Colin O'Dell",
+                    "email": "[email protected]",
+                    "homepage": "https://www.colinodell.com",
+                    "role": "Lead Developer"
+                },
+                {
+                    "name": "Nick Cernis",
+                    "email": "[email protected]",
+                    "homepage": "http://modernnerd.net",
+                    "role": "Original Author"
+                }
+            ],
+            "description": "An HTML-to-markdown conversion helper for PHP",
+            "homepage": "https://github.com/thephpleague/html-to-markdown",
+            "keywords": [
+                "html",
+                "markdown"
+            ],
+            "support": {
+                "issues": "https://github.com/thephpleague/html-to-markdown/issues",
+                "source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.1"
+            },
+            "funding": [
+                {
+                    "url": "https://www.colinodell.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.paypal.me/colinpodell/10.00",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/colinodell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2023-07-12T21:21:09+00:00"
+        },
         {
             "name": "lizhichao/one-sm",
             "version": "1.10",
@@ -3967,20 +4056,20 @@
         },
         {
             "name": "psr/http-factory",
-            "version": "1.0.2",
+            "version": "1.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/http-factory.git",
-                "reference": "e616d01114759c4c489f93b099585439f795fe35"
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
-                "reference": "e616d01114759c4c489f93b099585439f795fe35",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+                "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.0.0",
+                "php": ">=7.1",
                 "psr/http-message": "^1.0 || ^2.0"
             },
             "type": "library",
@@ -4004,7 +4093,7 @@
                     "homepage": "https://www.php-fig.org/"
                 }
             ],
-            "description": "Common interfaces for PSR-7 HTTP message factories",
+            "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
             "keywords": [
                 "factory",
                 "http",
@@ -4016,9 +4105,9 @@
                 "response"
             ],
             "support": {
-                "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+                "source": "https://github.com/php-fig/http-factory"
             },
-            "time": "2023-04-10T20:10:41+00:00"
+            "time": "2024-04-15T12:06:14+00:00"
         },
         {
             "name": "psr/http-message",
@@ -5156,16 +5245,16 @@
         },
         {
             "name": "stripe/stripe-php",
-            "version": "v14.5.0",
+            "version": "v14.6.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/stripe/stripe-php.git",
-                "reference": "5e0a29ebbe5506126b2cb26f560b0f22d73f1857"
+                "reference": "cc4108812a0b6db06041e520fe340f6d8ecfeff1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/5e0a29ebbe5506126b2cb26f560b0f22d73f1857",
-                "reference": "5e0a29ebbe5506126b2cb26f560b0f22d73f1857",
+                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/cc4108812a0b6db06041e520fe340f6d8ecfeff1",
+                "reference": "cc4108812a0b6db06041e520fe340f6d8ecfeff1",
                 "shasum": ""
             },
             "require": {
@@ -5209,9 +5298,9 @@
             ],
             "support": {
                 "issues": "https://github.com/stripe/stripe-php/issues",
-                "source": "https://github.com/stripe/stripe-php/tree/v14.5.0"
+                "source": "https://github.com/stripe/stripe-php/tree/v14.6.0"
             },
-            "time": "2024-05-02T20:56:57+00:00"
+            "time": "2024-05-09T19:39:17+00:00"
         },
         {
             "name": "symfony/clock",
@@ -7346,16 +7435,16 @@
         },
         {
             "name": "friendsofphp/php-cs-fixer",
-            "version": "v3.56.0",
+            "version": "v3.56.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
-                "reference": "4429303e62a4ce583ddfe64ff5c34c76bcf74931"
+                "reference": "69c6168ae8bc96dc656c7f6c7271120a68ae5903"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/4429303e62a4ce583ddfe64ff5c34c76bcf74931",
-                "reference": "4429303e62a4ce583ddfe64ff5c34c76bcf74931",
+                "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/69c6168ae8bc96dc656c7f6c7271120a68ae5903",
+                "reference": "69c6168ae8bc96dc656c7f6c7271120a68ae5903",
                 "shasum": ""
             },
             "require": {
@@ -7427,7 +7516,7 @@
             ],
             "support": {
                 "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
-                "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.56.0"
+                "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.56.1"
             },
             "funding": [
                 {
@@ -7435,7 +7524,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2024-05-07T15:50:05+00:00"
+            "time": "2024-05-10T11:31:15+00:00"
         },
         {
             "name": "justinrainbow/json-schema",

+ 74 - 65
config/settings.json

@@ -666,139 +666,148 @@
         "mark": "hCaptcha Secret"
     },
     {
-        "item": "enable_telegram",
-        "value": "0",
+        "item": "im_bot_group_notify_add_node",
+        "value": "1",
         "class": "im",
-        "is_public": 1,
+        "is_public": 0,
         "type": "bool",
-        "default": "0",
-        "mark": "是否启用Telegram机器人"
+        "default": "1",
+        "mark": "Enable IM bot group add node notify"
     },
     {
-        "item": "telegram_token",
-        "value": "",
+        "item": "im_bot_group_notify_update_node",
+        "value": "0",
         "class": "im",
         "is_public": 0,
-        "type": "string",
-        "default": "",
-        "mark": "Telegram Bot Token"
+        "type": "bool",
+        "default": "0",
+        "mark": "Enable IM bot group update node notify"
     },
     {
-        "item": "telegram_chatid",
-        "value": "-1",
+        "item": "im_bot_group_notify_delete_node",
+        "value": "0",
         "class": "im",
         "is_public": 0,
-        "type": "int",
-        "default": "-1",
-        "mark": "Telegram Group ID"
+        "type": "bool",
+        "default": "0",
+        "mark": "Enable IM bot group delete node notify"
     },
     {
-        "item": "enable_telegram_group_notify",
+        "item": "im_bot_group_notify_node_gfwed",
         "value": "0",
         "class": "im",
-        "is_public": 1,
+        "is_public": 0,
         "type": "bool",
         "default": "0",
-        "mark": "Enable Telegram group notify"
+        "mark": "Enable IM bot group node gfwed notify"
     },
     {
-        "item": "telegram_bot",
-        "value": "_bot",
+        "item": "im_bot_group_notify_node_ungfwed",
+        "value": "0",
         "class": "im",
-        "is_public": 1,
-        "type": "string",
-        "default": "_bot",
-        "mark": "Telegram 机器人账号"
+        "is_public": 0,
+        "type": "bool",
+        "default": "0",
+        "mark": "Enable IM bot group node ungfwed notify"
     },
     {
-        "item": "telegram_request_token",
-        "value": "",
+        "item": "im_bot_group_notify_node_online",
+        "value": "0",
         "class": "im",
         "is_public": 0,
-        "type": "string",
-        "default": "",
-        "mark": "Telegram Webhook 密钥"
+        "type": "bool",
+        "default": "0",
+        "mark": "Enable IM bot group node online notify"
     },
     {
-        "item": "telegram_add_node",
-        "value": "1",
+        "item": "im_bot_group_notify_node_offline",
+        "value": "0",
         "class": "im",
         "is_public": 0,
         "type": "bool",
-        "default": "1",
-        "mark": "是否启用Telegram机器人添加节点通知"
+        "default": "0",
+        "mark": "Enable IM bot group node offline notify"
     },
     {
-        "item": "telegram_update_node",
+        "item": "im_bot_group_notify_daily_job",
         "value": "1",
         "class": "im",
         "is_public": 0,
         "type": "bool",
         "default": "1",
-        "mark": "是否启用Telegram机器人修改节点通知"
+        "mark": "Enable IM bot group daily job notify"
     },
     {
-        "item": "telegram_delete_node",
-        "value": "1",
+        "item": "im_bot_group_notify_diary",
+        "value": "0",
         "class": "im",
         "is_public": 0,
         "type": "bool",
-        "default": "1",
-        "mark": "是否启用Telegram机器人删除节点通知"
+        "default": "0",
+        "mark": "Enable IM bot group diary notify"
     },
     {
-        "item": "telegram_node_gfwed",
+        "item": "im_bot_group_notify_ann_create",
         "value": "1",
         "class": "im",
         "is_public": 0,
         "type": "bool",
         "default": "1",
-        "mark": "是否启用Telegram机器人节点被墙通知"
+        "mark": "Enable IM bot group notify when new announcement is created"
     },
     {
-        "item": "telegram_node_ungfwed",
-        "value": "1",
+        "item": "im_bot_group_notify_ann_update",
+        "value": "0",
         "class": "im",
         "is_public": 0,
         "type": "bool",
-        "default": "1",
-        "mark": "是否启用Telegram机器人节点被墙恢复通知"
+        "default": "0",
+        "mark": "Enable IM bot group notify when announcement is updated"
     },
     {
-        "item": "telegram_node_online",
-        "value": "1",
+        "item": "telegram_token",
+        "value": "",
         "class": "im",
         "is_public": 0,
-        "type": "bool",
-        "default": "1",
-        "mark": "是否启用Telegram机器人节点恢复上线通知"
+        "type": "string",
+        "default": "",
+        "mark": "Telegram Bot Token"
     },
     {
-        "item": "telegram_node_offline",
-        "value": "1",
+        "item": "telegram_chatid",
+        "value": "-1",
         "class": "im",
         "is_public": 0,
-        "type": "bool",
-        "default": "1",
-        "mark": "是否启用Telegram机器人节点离线通知"
+        "type": "int",
+        "default": "-1",
+        "mark": "Telegram Chat ID"
     },
     {
-        "item": "telegram_daily_job",
-        "value": "1",
+        "item": "enable_telegram_group_notify",
+        "value": "0",
         "class": "im",
-        "is_public": 0,
+        "is_public": 1,
         "type": "bool",
-        "default": "1",
-        "mark": "是否启用Telegram机器人每日任务通知"
+        "default": "0",
+        "mark": "Enable Telegram group notify"
     },
     {
-        "item": "telegram_diary",
-        "value": "1",
+        "item": "telegram_bot",
+        "value": "_bot",
+        "class": "im",
+        "is_public": 1,
+        "type": "string",
+        "default": "_bot",
+        "mark": "Telegram 机器人账号"
+    },
+    {
+        "item": "telegram_request_token",
+        "value": "",
         "class": "im",
         "is_public": 0,
-        "type": "bool",
-        "default": "1",
-        "mark": "是否启用Telegram机器人系统运行状况通知"
+        "type": "string",
+        "default": "",
+        "mark": "Telegram Webhook 密钥"
     },
     {
         "item": "telegram_unbind_kick_member",

+ 2 - 1
resources/locale/en_US.php

@@ -16,9 +16,10 @@ return [
         'node_online' => '%node_name% is back online',
         'node_offline' => '%node_name% has gone offline',
         'daily_job_run' => 'Successful execution of daily tasks',
-        'diary' => 'Number of people checking in today: %getTodayCheckinUser%' . PHP_EOL . 'Total traffic used today: %lastday_total%',
+        'diary' => 'Number of people checking in today: %checkin_user%' . PHP_EOL . 'Total traffic used today: %lastday_total%',
         'user_not_bind' => 'You have not bound your account to this website. You can enter the **Edit Account** of the website and bind your account in the lower right corner.',
         'user_join_welcome_free' => 'Welcome %user_name%',
         'user_join_welcome_paid' => 'Welcome VIP%user_class% user %user_name% to join the group',
+        'test_message' => 'Test message',
     ],
 ];

+ 2 - 1
resources/locale/ja_JP.php

@@ -16,9 +16,10 @@ return [
         'node_online' => '%node_name% はオンラインに戻りました',
         'node_offline' => '%node_name% にはいくつかの障害があります',
         'daily_job_run' => '日次タスクの正常な実行',
-        'diary' => '今日チェックインした人の数: %getTodayCheckinUser%' . PHP_EOL . '今日使用された合計トラフィック: %lastday_total%',
+        'diary' => '今日チェックインした人の数: %checkin_user%' . PHP_EOL . '今日使用された合計トラフィック: %lastday_total%',
         'user_not_bind' => 'アカウントをこの Web サイトにバインドしていません。Web サイトの **データ編集** に入り、右下隅でアカウントをバインドできます。',
         'user_join_welcome_free' => 'ようこそ %user_name%',
         'user_join_welcome_paid' => 'VIP%user_class% ユーザー %user_name% のグループへの参加を歓迎します',
+        'test_message' => 'テストメッセージ',
     ],
 ];

+ 2 - 1
resources/locale/zh_CN.php

@@ -16,9 +16,10 @@ return [
         'node_online' => '%node_name% 节点恢复上线',
         'node_offline' => '%node_name% 节点出现了一些故障',
         'daily_job_run' => '成功执行每日任务',
-        'diary' => '今日签到人数:%getTodayCheckinUser%' . PHP_EOL . '今日使用总流量:%lastday_total%',
+        'diary' => '今日签到人数:%checkin_user%' . PHP_EOL . '今日使用总流量:%lastday_total%',
         'user_not_bind' => '您未绑定本站账号,您可以进入网站的 **资料编辑**,在右下方绑定您的账号。',
         'user_join_welcome_free' => '欢迎 %user_name%',
         'user_join_welcome_paid' => '欢迎 VIP%user_class% 用户 %user_name% 加入群组',
+        'test_message' => '测试消息',
     ],
 ];

+ 2 - 1
resources/locale/zh_TW.php

@@ -16,9 +16,10 @@ return [
         'node_online' => '%node_name% 恢復上線',
         'node_offline' => '%node_name% 出現了一些故障',
         'daily_job_run' => '成功執行每日任務',
-        'diary' => '今日簽到人數:%getTodayCheckinUser%' . PHP_EOL . '今日使用總流量:%lastday_total%',
+        'diary' => '今日簽到人數:%checkin_user%' . PHP_EOL . '今日使用總流量:%lastday_total%',
         'user_not_bind' => '您未綁定本站帳號,您可以進入網站的 **資料編輯**,在右下方綁定您的帳號。',
         'user_join_welcome_free' => '歡迎 %user_name%',
         'user_join_welcome_paid' => '歡迎 VIP%user_class% 使用者 %user_name% 加入群組',
+        'test_message' => '測試訊息',
     ],
 ];

+ 126 - 71
resources/views/tabler/admin/setting/im.tpl

@@ -31,7 +31,7 @@
                         <div class="card-header">
                             <ul class="nav nav-tabs card-header-tabs" data-bs-toggle="tabs">
                                 <li class="nav-item">
-                                    <a href="#notification" class="nav-link active" data-bs-toggle="tab">通知设定</a>
+                                    <a href="#notification" class="nav-link active" data-bs-toggle="tab">Notification</a>
                                 </li>
                                 <li class="nav-item">
                                     <a href="#telegram" class="nav-link" data-bs-toggle="tab">Telegram Bot</a>
@@ -49,150 +49,205 @@
                                 <div class="tab-pane active show" id="notification">
                                     <div class="card-body">
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">添加节点通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Node Addition
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_add_node" class="col form-select"
-                                                        value="{$settings['telegram_add_node']}">
+                                                <select id="im_bot_group_notify_add_node" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_add_node']}">
                                                     <option value="0"
-                                                            {if ! $settings['telegram_add_node']}selected{/if}>关闭
+                                                            {if ! $settings['im_bot_group_notify_add_node']}selected{/if}>
+                                                        False
                                                     </option>
-                                                    <option value="1" {if $settings['telegram_add_node']}selected{/if}>
-                                                        开启
+                                                    <option value="1"
+                                                            {if $settings['im_bot_group_notify_add_node']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">修改节点通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Node Update
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_update_node" class="col form-select"
-                                                        value="{$settings['telegram_update_node']}">
+                                                <select id="im_bot_group_notify_update_node" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_update_node']}">
                                                     <option value="0"
-                                                            {if ! $settings['telegram_update_node']}selected{/if}>关闭
+                                                            {if ! $settings['im_bot_group_notify_update_node']}selected{/if}>
+                                                        False
                                                     </option>
                                                     <option value="1"
-                                                            {if $settings['telegram_update_node']}selected{/if}>开启
+                                                            {if $settings['im_bot_group_notify_update_node']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">删除节点通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Node Deletion
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_delete_node" class="col form-select"
-                                                        value="{$settings['telegram_delete_node']}">
+                                                <select id="im_bot_group_notify_delete_node" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_delete_node']}">
                                                     <option value="0"
-                                                            {if ! $settings['telegram_delete_node']}selected{/if}>关闭
+                                                            {if ! $settings['im_bot_group_notify_delete_node']}selected{/if}>
+                                                        False
                                                     </option>
                                                     <option value="1"
-                                                            {if $settings['telegram_delete_node']}selected{/if}>开启
+                                                            {if $settings['im_bot_group_notify_delete_node']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">节点被墙通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Node GFWed
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_node_gfwed" class="col form-select"
-                                                        value="{$settings['telegram_node_gfwed']}">
+                                                <select id="im_bot_group_notify_node_gfwed" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_node_gfwed']}">
                                                     <option value="0"
-                                                            {if ! $settings['telegram_node_gfwed']}selected{/if}>关闭
+                                                            {if ! $settings['im_bot_group_notify_node_gfwed']}selected{/if}>
+                                                        False
                                                     </option>
                                                     <option value="1"
-                                                            {if $settings['telegram_node_gfwed']}selected{/if}>开启
+                                                            {if $settings['im_bot_group_notify_node_gfwed']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">节点被墙恢复通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Node UnGFWed
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_node_ungfwed" class="col form-select"
-                                                        value="{$settings['telegram_node_ungfwed']}">
+                                                <select id="im_bot_group_notify_node_ungfwed" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_node_ungfwed']}">
                                                     <option value="0"
-                                                            {if ! $settings['telegram_node_ungfwed']}selected{/if}>关闭
+                                                            {if ! $settings['im_bot_group_notify_node_ungfwed']}selected{/if}>
+                                                        False
                                                     </option>
                                                     <option value="1"
-                                                            {if $settings['telegram_node_ungfwed']}selected{/if}>开启
+                                                            {if $settings['im_bot_group_notify_node_ungfwed']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">节点离线通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Node Online
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_node_offline" class="col form-select"
-                                                        value="{$settings['telegram_node_offline']}">
+                                                <select id="im_bot_group_notify_node_online" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_node_online']}">
                                                     <option value="0"
-                                                            {if ! $settings['telegram_node_offline']}selected{/if}>关闭
+                                                            {if ! $settings['im_bot_group_notify_node_online']}selected{/if}>
+                                                        False
                                                     </option>
                                                     <option value="1"
-                                                            {if $settings['telegram_node_offline']}selected{/if}>开启
+                                                            {if $settings['im_bot_group_notify_node_online']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">节点上线通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Node Offline
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_node_online" class="col form-select"
-                                                        value="{$settings['telegram_node_online']}">
+                                                <select id="im_bot_group_notify_node_offline" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_node_offline']}">
                                                     <option value="0"
-                                                            {if ! $settings['telegram_node_online']}selected{/if}>关闭
+                                                            {if ! $settings['im_bot_group_notify_node_offline']}selected{/if}>
+                                                        False
                                                     </option>
                                                     <option value="1"
-                                                            {if $settings['telegram_node_online']}selected{/if}>开启
+                                                            {if $settings['im_bot_group_notify_node_offline']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">每日任务通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Daily Job
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_daily_job" class="col form-select"
-                                                        value="{$settings['telegram_daily_job']}">
+                                                <select id="im_bot_group_notify_daily_job" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_daily_job']}">
                                                     <option value="0"
-                                                            {if ! $settings['telegram_daily_job']}selected{/if}>关闭
+                                                            {if ! $settings['im_bot_group_notify_daily_job']}selected{/if}>
+                                                        False
                                                     </option>
-                                                    <option value="1" {if $settings['telegram_daily_job']}selected{/if}>
-                                                        开启
+                                                    <option value="1"
+                                                            {if $settings['im_bot_group_notify_daily_job']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">系统运行状况通知</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                System Dairy
+                                            </label>
                                             <div class="col">
-                                                <select id="telegram_diary" class="col form-select"
-                                                        value="{$settings['telegram_diary']}">
-                                                    <option value="0" {if ! $settings['telegram_diary']}selected{/if}>
-                                                        关闭
+                                                <select id="im_bot_group_notify_diary" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_diary']}">
+                                                    <option value="0" {if ! $settings['im_bot_group_notify_diary']}selected{/if}>
+                                                        False
                                                     </option>
-                                                    <option value="1" {if $settings['telegram_diary']}selected{/if}>
-                                                        开启
+                                                    <option value="1" {if $settings['im_bot_group_notify_diary']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
-                                    </div>
-                                </div>
-                                <div class="tab-pane" id="telegram">
-                                    <div class="card-body">
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">是否启用 Telegram
-                                                机器人</label>
+                                            <label class="form-label col-3 col-form-label">
+                                                Announcement Creation
+                                            </label>
                                             <div class="col">
-                                                <select id="enable_telegram" class="col form-select"
-                                                        value="{$settings['enable_telegram']}">
-                                                    <option value="0" {if ! $settings['enable_telegram']}selected{/if}>
-                                                        关闭
+                                                <select id="im_bot_group_notify_ann_create" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_ann_create']}">
+                                                    <option value="0"
+                                                            {if ! $settings['im_bot_group_notify_ann_create']}selected{/if}>
+                                                        False
                                                     </option>
-                                                    <option value="1" {if $settings['enable_telegram']}selected{/if}>
-                                                        开启
+                                                    <option value="1"
+                                                            {if $settings['im_bot_group_notify_ann_create']}selected{/if}>
+                                                        True
                                                     </option>
                                                 </select>
                                             </div>
                                         </div>
+                                        <div class="form-group mb-3 row">
+                                            <label class="form-label col-3 col-form-label">
+                                                Announcement Update
+                                            </label>
+                                            <div class="col">
+                                                <select id="im_bot_group_notify_ann_update" class="col form-select"
+                                                        value="{$settings['im_bot_group_notify_ann_update']}">
+                                                    <option value="0"
+                                                            {if ! $settings['im_bot_group_notify_ann_update']}selected{/if}>
+                                                        False
+                                                    </option>
+                                                    <option value="1"
+                                                            {if $settings['im_bot_group_notify_ann_update']}selected{/if}>
+                                                        True
+                                                    </option>
+                                                </select>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="tab-pane" id="telegram">
+                                    <div class="card-body">
                                         <div class="form-group mb-3 row">
                                             <label class="form-label col-3 col-form-label">Bot Token</label>
                                             <div class="col">
@@ -338,8 +393,8 @@
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">Telegram User ID</label>
-                                            <input type="text" class="form-control" id="telegram_user_id" value="">
+                                            <label class="form-label col-3 col-form-label">Telegram Chat ID(Group/DM)</label>
+                                            <input type="text" class="form-control" id="telegram_chat_id" value="">
                                             <div class="row my-3">
                                                 <div class="col">
                                                     <button id="test-telegram" class="btn btn-primary">
@@ -404,8 +459,8 @@
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">Discord User ID</label>
-                                            <input type="text" class="form-control" id="discord_user_id" value="">
+                                            <label class="form-label col-3 col-form-label">Discord User ID/Channel ID</label>
+                                            <input type="text" class="form-control" id="discord_channel_id" value="">
                                             <div class="row my-3">
                                                 <div class="col">
                                                     <button id="test-discord" class="btn btn-primary">
@@ -470,8 +525,8 @@
                                             </div>
                                         </div>
                                         <div class="form-group mb-3 row">
-                                            <label class="form-label col-3 col-form-label">Slack User ID</label>
-                                            <input type="text" class="form-control" id="slack_user_id" value="">
+                                            <label class="form-label col-3 col-form-label">Slack User ID/Channel ID</label>
+                                            <input type="text" class="form-control" id="slack_channel_id" value="">
                                             <div class="row my-3">
                                                 <div class="col">
                                                     <button id="test-slack" class="btn btn-primary">
@@ -518,7 +573,7 @@
                     type: 'POST',
                     dataType: "json",
                     data: {
-                        telegram_user_id: $('#telegram_user_id').val(),
+                        telegram_chat_id: $('#telegram_chat_id').val(),
                     },
                     success: function (data) {
                         if (data.ret === 1) {
@@ -538,7 +593,7 @@
                     type: 'POST',
                     dataType: "json",
                     data: {
-                        discord_user_id: $('#discord_user_id').val(),
+                        discord_channel_id: $('#discord_channel_id').val(),
                     },
                     success: function (data) {
                         if (data.ret === 1) {
@@ -558,7 +613,7 @@
                     type: 'POST',
                     dataType: "json",
                     data: {
-                        slack_user_id: $('#slack_user_id').val(),
+                        slack_channel_id: $('#slack_channel_id').val(),
                     },
                     success: function (data) {
                         if (data.ret === 1) {

+ 2 - 2
resources/views/tabler/datatable.tpl

@@ -1,5 +1,5 @@
-<link href="//cdn.datatables.net/v/bs5/dt-2.0.4/datatables.min.css" rel="stylesheet"/>
-<script src="//cdn.datatables.net/v/bs5/dt-2.0.4/datatables.min.js"></script>
+<link href="//cdn.datatables.net/v/bs5/dt-2.0.7/datatables.min.css" rel="stylesheet"/>
+<script src="//cdn.datatables.net/v/bs5/dt-2.0.7/datatables.min.js"></script>
 
 <script>
     let tableConfig = {

+ 2 - 2
resources/views/tabler/tinymce.tpl

@@ -1,8 +1,8 @@
-<script src="//cdnjs.cloudflare.com/ajax/libs/tinymce/7.0.1/tinymce.min.js"></script>
+<script src="//cdnjs.cloudflare.com/ajax/libs/tinymce/7.1.0/tinymce.min.js"></script>
 
 <script>
     document.addEventListener("DOMContentLoaded", function () {
-        tinyMCE.baseURL = '//cdnjs.cloudflare.com/ajax/libs/tinymce/7.0.1/';
+        tinyMCE.baseURL = '//cdnjs.cloudflare.com/ajax/libs/tinymce/7.1.0/';
         tinyMCE.suffix = '.min';
         tinyMCE.init({
             selector: '#tinymce',

+ 4 - 4
src/Command/Cron.php

@@ -68,14 +68,14 @@ EOL;
                 $jobs->removeInactiveUserLinkAndInvite();
             }
 
-            if (Config::obtain('telegram_diary')) {
-                $jobs->sendTelegramDiary();
+            if (Config::obtain('im_bot_group_notify_diary')) {
+                $jobs->sendDiaryNotification();
             }
 
             $jobs->resetTodayBandwidth();
 
-            if (Config::obtain('telegram_daily_job')) {
-                $jobs->sendTelegramDailyJob();
+            if (Config::obtain('im_bot_group_notify_daily_job')) {
+                $jobs->sendDailyJobNotification();
             }
 
             (new Config())->where('item', 'last_daily_job_time')->update([

+ 21 - 22
src/Controllers/Admin/AnnController.php

@@ -9,15 +9,15 @@ use App\Models\Ann;
 use App\Models\Config;
 use App\Models\EmailQueue;
 use App\Models\User;
-use App\Services\IM\Telegram;
+use App\Services\Notification;
 use App\Utils\Tools;
 use Exception;
+use GuzzleHttp\Exception\GuzzleException;
+use League\HTMLToMarkdown\HtmlConverter;
 use Psr\Http\Message\ResponseInterface;
 use Slim\Http\Response;
 use Slim\Http\ServerRequest;
 use Telegram\Bot\Exceptions\TelegramSDKException;
-use function str_replace;
-use function strip_tags;
 use function time;
 use const PHP_EOL;
 
@@ -72,16 +72,7 @@ final class AnnController extends BaseController
     {
         $email_notify_class = (int) $request->getParam('email_notify_class');
         $email_notify = $request->getParam('email_notify') === 'true' ? 1 : 0;
-
-        $content = strip_tags(
-            str_replace(
-                ['<p>','</p>'],
-                ['','<br><br>'],
-                $request->getParam('content')
-            ),
-            ['br', 'a', 'strong']
-        );
-        $subject = $_ENV['appName'] . ' - 新公告发布';
+        $content = $request->getParam('content');
 
         if ($content !== '') {
             $ann = new Ann();
@@ -96,9 +87,10 @@ final class AnnController extends BaseController
             }
         }
 
-        if ($email_notify) {
+        if ($email_notify && $content !== '') {
             $users = (new User())->where('class', '>=', $email_notify_class)
                 ->get();
+            $subject = $_ENV['appName'] . ' - 新公告发布';
 
             foreach ($users as $user) {
                 (new EmailQueue())->add(
@@ -113,13 +105,16 @@ final class AnnController extends BaseController
             }
         }
 
-        if (Config::obtain('enable_telegram')) {
+        if (Config::obtain('im_bot_group_notify_ann_create') && $content !== '') {
+            $converter = new HtmlConverter(['strip_tags' => true]);
+            $content = $converter->convert($content);
+
             try {
-                (new Telegram())->sendHtml(0, '新公告:' . PHP_EOL . $content);
-            } catch (TelegramSDKException) {
+                Notification::notifyUserGroup('新公告:' . PHP_EOL . $content);
+            } catch (TelegramSDKException | GuzzleException) {
                 return $response->withJson([
                     'ret' => 0,
-                    'msg' => $email_notify === 1 ? '公告添加成功,邮件发送成功,Telegram发送失败' : '公告添加成功,Telegram发送失败',
+                    'msg' => $email_notify === 1 ? '公告添加成功,邮件发送成功,IM Bot 发送失败' : '公告添加成功,IM Bot 发送失败',
                 ]);
             }
         }
@@ -138,6 +133,7 @@ final class AnnController extends BaseController
     public function edit(ServerRequest $request, Response $response, array $args): ResponseInterface
     {
         $ann = (new Ann())->find($args['id']);
+
         return $response->write(
             $this->view()
                 ->assign('ann', $ann)
@@ -161,13 +157,16 @@ final class AnnController extends BaseController
             ]);
         }
 
-        if (Config::obtain('enable_telegram')) {
+        if (Config::obtain('im_bot_group_notify_ann_update')) {
+            $converter = new HtmlConverter(['strip_tags' => true]);
+            $content = $converter->convert($ann->content);
+
             try {
-                (new Telegram())->sendHtml(0, '公告更新:' . PHP_EOL . $request->getParam('content'));
-            } catch (TelegramSDKException) {
+                Notification::notifyUserGroup('公告更新:' . PHP_EOL . $content);
+            } catch (TelegramSDKException | GuzzleException) {
                 return $response->withJson([
                     'ret' => 0,
-                    'msg' => '公告更新成功,Telegram发送失败',
+                    'msg' => '公告更新成功,IM Bot 发送失败',
                 ]);
             }
         }

+ 2 - 6
src/Controllers/Admin/DetectRuleController.php

@@ -6,12 +6,10 @@ namespace App\Controllers\Admin;
 
 use App\Controllers\BaseController;
 use App\Models\DetectRule;
-use App\Services\IM\Telegram;
 use Exception;
 use Psr\Http\Message\ResponseInterface;
 use Slim\Http\Response;
 use Slim\Http\ServerRequest;
-use Telegram\Bot\Exceptions\TelegramSDKException;
 
 final class DetectRuleController extends BaseController
 {
@@ -68,9 +66,6 @@ final class DetectRuleController extends BaseController
         );
     }
 
-    /**
-     * @throws TelegramSDKException
-     */
     public function add(ServerRequest $request, Response $response, array $args): ResponseInterface
     {
         $rule = new DetectRule();
@@ -86,7 +81,6 @@ final class DetectRuleController extends BaseController
             ]);
         }
 
-        (new Telegram())->sendMarkdown(0, '有新的审计规则:' . $rule->name);
         return $response->withJson([
             'ret' => 1,
             'msg' => '添加成功',
@@ -97,12 +91,14 @@ final class DetectRuleController extends BaseController
     {
         $id = $args['id'];
         $rule = (new DetectRule())->find($id);
+
         if (! $rule->delete()) {
             return $response->withJson([
                 'ret' => 0,
                 'msg' => '删除失败',
             ]);
         }
+
         return $response->withJson([
             'ret' => 1,
             'msg' => '删除成功',

+ 14 - 16
src/Controllers/Admin/NodeController.php

@@ -8,8 +8,9 @@ use App\Controllers\BaseController;
 use App\Models\Config;
 use App\Models\Node;
 use App\Services\I18n;
-use App\Services\IM\Telegram;
+use App\Services\Notification;
 use App\Utils\Tools;
+use GuzzleHttp\Exception\GuzzleException;
 use Psr\Http\Message\ResponseInterface;
 use Slim\Http\Response;
 use Slim\Http\ServerRequest;
@@ -131,20 +132,19 @@ final class NodeController extends BaseController
             ]);
         }
 
-        if (Config::obtain('telegram_add_node')) {
+        if (Config::obtain('im_bot_group_notify_add_node')) {
             try {
-                (new Telegram())->send(
-                    0,
+                Notification::notifyUserGroup(
                     str_replace(
                         '%node_name%',
                         $request->getParam('name'),
                         I18n::trans('bot.node_added', $_ENV['locale'])
                     )
                 );
-            } catch (TelegramSDKException) {
+            } catch (TelegramSDKException | GuzzleException) {
                 return $response->withJson([
                     'ret' => 1,
-                    'msg' => '添加成功,但 Telegram 通知失败',
+                    'msg' => '添加成功,但 IM Bot 通知失败',
                     'node_id' => $node->id,
                 ]);
             }
@@ -225,20 +225,19 @@ final class NodeController extends BaseController
             ]);
         }
 
-        if (Config::obtain('telegram_update_node')) {
+        if (Config::obtain('im_bot_group_notify_update_node')) {
             try {
-                (new Telegram())->send(
-                    0,
+                Notification::notifyUserGroup(
                     str_replace(
                         '%node_name%',
                         $request->getParam('name'),
                         I18n::trans('bot.node_updated', $_ENV['locale'])
                     )
                 );
-            } catch (TelegramSDKException) {
+            } catch (TelegramSDKException | GuzzleException) {
                 return $response->withJson([
                     'ret' => 1,
-                    'msg' => '修改成功,但 Telegram 通知失败',
+                    'msg' => '修改成功,但 IM Bot 通知失败',
                 ]);
             }
         }
@@ -287,20 +286,19 @@ final class NodeController extends BaseController
             ]);
         }
 
-        if (Config::obtain('telegram_delete_node')) {
+        if (Config::obtain('im_bot_group_notify_delete_node')) {
             try {
-                (new Telegram())->send(
-                    0,
+                Notification::notifyUserGroup(
                     str_replace(
                         '%node_name%',
                         $node->name,
                         I18n::trans('bot.node_deleted', $_ENV['locale'])
                     )
                 );
-            } catch (TelegramSDKException) {
+            } catch (TelegramSDKException | GuzzleException) {
                 return $response->withJson([
                     'ret' => 1,
-                    'msg' => '删除成功,但Telegram通知失败',
+                    'msg' => '删除成功,但 IM Bot 通知失败',
                 ]);
             }
         }

+ 20 - 19
src/Controllers/Admin/Setting/ImController.php

@@ -6,6 +6,7 @@ namespace App\Controllers\Admin\Setting;
 
 use App\Controllers\BaseController;
 use App\Models\Config;
+use App\Services\I18n;
 use App\Services\IM\Discord;
 use App\Services\IM\Slack;
 use App\Services\IM\Telegram;
@@ -20,17 +21,18 @@ final class ImController extends BaseController
 {
     private static array $update_field = [
         // TODO: rename these to im service independent
-        'telegram_add_node',
-        'telegram_update_node',
-        'telegram_delete_node',
-        'telegram_node_gfwed',
-        'telegram_node_ungfwed',
-        'telegram_node_online',
-        'telegram_node_offline',
-        'telegram_daily_job',
-        'telegram_diary',
+        'im_bot_group_notify_add_node',
+        'im_bot_group_notify_update_node',
+        'im_bot_group_notify_delete_node',
+        'im_bot_group_notify_node_gfwed',
+        'im_bot_group_notify_node_ungfwed',
+        'im_bot_group_notify_node_online',
+        'im_bot_group_notify_node_offline',
+        'im_bot_group_notify_daily_job',
+        'im_bot_group_notify_diary',
+        'im_bot_group_notify_ann_create',
+        'im_bot_group_notify_ann_update',
         // Telegram
-        'enable_telegram',
         'telegram_token',
         'telegram_chatid',
         'enable_telegram_group_notify',
@@ -59,7 +61,6 @@ final class ImController extends BaseController
         'enable_slack_channel_notify',
     ];
 
-    private static string $test_msg = '这是一条测试消息';
     private static string $success_msg = '测试信息发送成功';
     private static string $err_msg = '测试信息发送失败';
 
@@ -99,8 +100,8 @@ final class ImController extends BaseController
     {
         try {
             (new Telegram())->send(
-                $request->getParam('telegram_user_id'),
-                $this::$test_msg,
+                (int) $request->getParam('telegram_chat_id'),
+                I18n::trans('bot.test_message', $_ENV['locale']),
             );
         } catch (TelegramSDKException|Exception $e) {
             return $response->withJson([
@@ -119,8 +120,8 @@ final class ImController extends BaseController
     {
         try {
             (new Discord())->send(
-                $request->getParam('discord_user_id'),
-                $this::$test_msg,
+                (int) $request->getParam('discord_channel_id'),
+                I18n::trans('bot.test_message', $_ENV['locale']),
             );
         } catch (GuzzleException|Exception $e) {
             return $response->withJson([
@@ -131,7 +132,7 @@ final class ImController extends BaseController
 
         return $response->withJson([
             'ret' => 1,
-            'msg' => '测试信息发送成功',
+            'msg' => $this::$success_msg,
         ]);
     }
 
@@ -139,8 +140,8 @@ final class ImController extends BaseController
     {
         try {
             (new Slack())->send(
-                $request->getParam('slack_user_id'),
-                $this::$test_msg,
+                (int) $request->getParam('slack_channel_id'),
+                I18n::trans('bot.test_message', $_ENV['locale']),
             );
         } catch (GuzzleException|Exception $e) {
             return $response->withJson([
@@ -151,7 +152,7 @@ final class ImController extends BaseController
 
         return $response->withJson([
             'ret' => 1,
-            'msg' => '测试信息发送成功',
+            'msg' => $this::$success_msg,
         ]);
     }
 }

+ 0 - 2
src/Controllers/Admin/SysLogController.php

@@ -70,8 +70,6 @@ final class SysLogController extends BaseController
 
     /**
      * 系统日志页面 AJAX
-     *
-     * @throws InvalidDatabaseException
      */
     public function ajax(ServerRequest $request, Response $response, array $args): ResponseInterface
     {

+ 1 - 1
src/Controllers/CallbackController.php

@@ -37,7 +37,7 @@ final class CallbackController extends BaseController
     {
         $token = $request->getQueryParam('token');
 
-        if (Config::obtain('enable_telegram') && $token === Config::obtain('telegram_request_token')) {
+        if (Config::obtain('telegram_token') !== '' && $token === Config::obtain('telegram_request_token')) {
             Telegram::process($request);
 
             return $response->withStatus(204);

+ 5 - 9
src/Controllers/User/InfoController.php

@@ -61,22 +61,18 @@ final class InfoController extends BaseController
             return ResponseHelper::error($response, '未填写邮箱');
         }
 
-        $email_check = Filter::checkEmailFilter($email);
-
-        if (! $email_check) {
+        if (! Filter::checkEmailFilter($new_email)) {
             return ResponseHelper::error($response, '无效的邮箱');
         }
 
-        $exist_user = (new User())->where('email', $new_email)->first();
-
-        if ($exist_user !== null) {
-            return ResponseHelper::error($response, '邮箱已经被使用了');
-        }
-
         if ($new_email === $old_email) {
             return ResponseHelper::error($response, '新邮箱不能和旧邮箱一样');
         }
 
+        if ((new User())->where('email', $new_email)->first() !== null) {
+            return ResponseHelper::error($response, '邮箱已经被使用了');
+        }
+
         if (Config::obtain('reg_email_verify')) {
             $redis = (new Cache())->initRedis();
             $email_verify_code = $request->getParam('emailcode');

+ 1 - 1
src/Models/User.php

@@ -302,7 +302,7 @@ final class User extends Model
             $text .= '今日使用:' . $lastday_traffic;
 
             try {
-                IM::send($this->im_value, $text, $this->im_type);
+                IM::send((int) $this->im_value, $text, $this->im_type);
             } catch (GuzzleException|TelegramSDKException $e) {
                 echo $e->getMessage() . PHP_EOL;
             }

+ 1 - 1
src/Services/Bot/Telegram/Callback.php

@@ -66,7 +66,7 @@ final class Callback
     /**
      * 触发源信息 ID
      */
-    private $message_id;
+    private int $message_id;
 
     /**
      * 源消息是否可编辑

+ 77 - 70
src/Services/Cron.php

@@ -17,7 +17,6 @@ use App\Models\Paylist;
 use App\Models\SubscribeLog;
 use App\Models\User;
 use App\Models\UserMoneyLog;
-use App\Services\IM\Telegram;
 use App\Utils\Tools;
 use DateTime;
 use Exception;
@@ -99,17 +98,17 @@ final class Cron
                     echo $e->getMessage() . PHP_EOL;
                 }
 
-                if (Config::obtain('telegram_node_offline')) {
-                    $notice_text = str_replace(
-                        '%node_name%',
-                        $node->name,
-                        I18n::trans('bot.node_offline', $_ENV['locale'])
-                    );
-
+                if (Config::obtain('im_bot_group_notify_node_offline')) {
                     try {
-                        (new Telegram())->send(0, $notice_text);
-                    } catch (TelegramSDKException $e) {
-                        echo $e->getMessage();
+                        Notification::notifyUserGroup(
+                            str_replace(
+                                '%node_name%',
+                                $node->name,
+                                I18n::trans('bot.node_offline', $_ENV['locale'])
+                            ),
+                        );
+                    } catch (TelegramSDKException | GuzzleException $e) {
+                        echo $e->getMessage() . PHP_EOL;
                     }
                 }
 
@@ -131,17 +130,17 @@ final class Cron
                     echo $e->getMessage() . PHP_EOL;
                 }
 
-                if (Config::obtain('telegram_node_online')) {
-                    $notice_text = str_replace(
-                        '%node_name%',
-                        $node->name,
-                        I18n::trans('bot.node_online', $_ENV['locale'])
-                    );
-
+                if (Config::obtain('im_bot_group_notify_node_online')) {
                     try {
-                        (new Telegram())->send(0, $notice_text);
-                    } catch (TelegramSDKException $e) {
-                        echo $e->getMessage();
+                        Notification::notifyUserGroup(
+                            str_replace(
+                                '%node_name%',
+                                $node->name,
+                                I18n::trans('bot.node_online', $_ENV['locale'])
+                            ),
+                        );
+                    } catch (TelegramSDKException | GuzzleException $e) {
+                        echo $e->getMessage() . PHP_EOL;
                     }
                 }
 
@@ -488,32 +487,37 @@ final class Cron
         $today = strtotime('00:00:00');
         $paylists = (new Paylist())->where('status', 1)
             ->whereBetween('datetime', [strtotime('-1 day', $today), $today])->get();
-        $text_html = '<table border=1><tr><td>金额</td><td>用户ID</td><td>用户名</td><td>充值时间</td>';
-
-        foreach ($paylists as $paylist) {
-            $text_html .= '<tr>';
-            $text_html .= '<td>' . $paylist->total . '</td>';
-            $text_html .= '<td>' . $paylist->userid . '</td>';
-            $text_html .= '<td>' . (new User())->find($paylist->userid)->user_name . '</td>';
-            $text_html .= '<td>' . Tools::toDateTime((int) $paylist->datetime) . '</td>';
-            $text_html .= '</tr>';
-        }
 
-        $text_html .= '</table>';
-        $text_html .= '<br>昨日总收入笔数:' . count($paylists) . '<br>昨日总收入金额:' . $paylists->sum('total');
-        echo 'Sending daily finance email to admin user' . PHP_EOL;
+        if (count($paylists) > 0) {
+            $text_html = '<table style="border=1;"><tr><td>金额</td><td>用户ID</td><td>用户名</td><td>充值时间</td>';
 
-        try {
-            Notification::notifyAdmin(
-                '财务日报',
-                $text_html,
-                'finance.tpl'
-            );
-        } catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
-            echo $e->getMessage() . PHP_EOL;
-        }
+            foreach ($paylists as $paylist) {
+                $text_html .= '<tr>';
+                $text_html .= '<td>' . $paylist->total . '</td>';
+                $text_html .= '<td>' . $paylist->userid . '</td>';
+                $text_html .= '<td>' . (new User())->find($paylist->userid)->user_name . '</td>';
+                $text_html .= '<td>' . Tools::toDateTime((int) $paylist->datetime) . '</td>';
+                $text_html .= '</tr>';
+            }
+
+            $text_html .= '</table>';
+            $text_html .= '<br>昨日总收入笔数:' . count($paylists) . '<br>昨日总收入金额:' . $paylists->sum('total');
+            echo 'Sending daily finance email to admin user' . PHP_EOL;
+
+            try {
+                Notification::notifyAdmin(
+                    '财务日报',
+                    $text_html,
+                    'finance.tpl'
+                );
+            } catch (GuzzleException|ClientExceptionInterface|TelegramSDKException $e) {
+                echo $e->getMessage() . PHP_EOL;
+            }
 
-        echo Tools::toDateTime(time()) . ' 成功发送财务日报' . PHP_EOL;
+            echo Tools::toDateTime(time()) . ' Successfully sent daily finance email' . PHP_EOL;
+        } else {
+            echo 'No paylist found' . PHP_EOL;
+        }
     }
 
     public static function sendWeeklyFinanceMail(): void
@@ -625,37 +629,40 @@ final class Cron
         echo Tools::toDateTime(time()) . ' 成功发送每日流量报告' . PHP_EOL;
     }
 
-    /**
-     * @throws TelegramSDKException
-     */
-    public static function sendTelegramDailyJob(): void
+    public static function sendDailyJobNotification(): void
     {
-        (new Telegram())->send(0, I18n::trans('bot.daily_job_run', $_ENV['locale']));
+        try {
+            Notification::notifyUserGroup(
+                I18n::trans('bot.daily_job_run', $_ENV['locale'])
+            );
+        } catch (TelegramSDKException | GuzzleException $e) {
+            echo $e->getMessage() . PHP_EOL;
+        }
 
-        echo Tools::toDateTime(time()) . ' 成功发送 Telegram 每日任务提示' . PHP_EOL;
+        echo Tools::toDateTime(time()) . ' Successfully sent daily job notification' . PHP_EOL;
     }
 
-    /**
-     * @throws TelegramSDKException
-     */
-    public static function sendTelegramDiary(): void
+    public static function sendDiaryNotification(): void
     {
-        (new Telegram())->send(
-            0,
-            str_replace(
-                [
-                    '%getTodayCheckinUser%',
-                    '%lastday_total%',
-                ],
-                [
-                    Analytics::getTodayCheckinUser(),
-                    Analytics::getTodayTrafficUsage(),
-                ],
-                I18n::trans('bot.diary', $_ENV['locale'])
-            )
-        );
-
-        echo Tools::toDateTime(time()) . ' 成功发送 Telegram 系统运行日志' . PHP_EOL;
+        try {
+            Notification::notifyUserGroup(
+                str_replace(
+                    [
+                        '%checkin_user%',
+                        '%lastday_total%',
+                    ],
+                    [
+                        Analytics::getTodayCheckinUser(),
+                        Analytics::getTodayTrafficUsage(),
+                    ],
+                    I18n::trans('bot.diary', $_ENV['locale'])
+                )
+            );
+        } catch (TelegramSDKException | GuzzleException $e) {
+            echo $e->getMessage() . PHP_EOL;
+        }
+
+        echo Tools::toDateTime(time()) . ' Successfully sent diary notification' . PHP_EOL;
     }
 
     public static function updateNodeIp(): void

+ 24 - 17
src/Services/Detect.php

@@ -9,7 +9,6 @@ use App\Models\DetectBanLog;
 use App\Models\DetectLog;
 use App\Models\Node;
 use App\Models\User;
-use App\Services\IM\Telegram;
 use App\Utils\Tools;
 use GuzzleHttp\Exception\GuzzleException;
 use Psr\Http\Client\ClientExceptionInterface;
@@ -60,14 +59,18 @@ final class Detect
                     echo $e->getMessage() . PHP_EOL;
                 }
 
-                if (Config::obtain('telegram_node_gfwed')) {
-                    $notice_text = str_replace(
-                        '%node_name%',
-                        $node->name,
-                        I18n::trans('bot.node_gfwed', $_ENV['locale'])
-                    );
-
-                    (new Telegram())->send(0, $notice_text);
+                if (Config::obtain('im_bot_group_notify_node_gfwed')) {
+                    try {
+                        Notification::notifyUserGroup(
+                            str_replace(
+                                '%node_name%',
+                                $node->name,
+                                I18n::trans('bot.node_gfwed', $_ENV['locale'])
+                            ),
+                        );
+                    } catch (TelegramSDKException | GuzzleException $e) {
+                        echo $e->getMessage() . PHP_EOL;
+                    }
                 }
 
                 $node->gfw_block = true;
@@ -89,14 +92,18 @@ final class Detect
                     echo $e->getMessage() . PHP_EOL;
                 }
 
-                if (Config::obtain('telegram_node_ungfwed')) {
-                    $notice_text = str_replace(
-                        '%node_name%',
-                        $node->name,
-                        I18n::trans('bot.node_ungfwed', $_ENV['locale'])
-                    );
-
-                    (new Telegram())->send(0, $notice_text);
+                if (Config::obtain('im_bot_group_notify_node_ungfwed')) {
+                    try {
+                        Notification::notifyUserGroup(
+                            str_replace(
+                                '%node_name%',
+                                $node->name,
+                                I18n::trans('bot.node_ungfwed', $_ENV['locale'])
+                            ),
+                        );
+                    } catch (TelegramSDKException | GuzzleException $e) {
+                        echo $e->getMessage() . PHP_EOL;
+                    }
                 }
 
                 $node->gfw_block = false;

+ 2 - 0
src/Services/Gateway/AlipayF2F.php

@@ -24,6 +24,8 @@ use voku\helper\AntiXSS;
 
 final class AlipayF2F extends Base
 {
+    private AlipayConfig $alipayConfig;
+
     public function __construct()
     {
         $this->antiXss = new AntiXSS();

+ 4 - 4
src/Services/IM.php

@@ -15,11 +15,11 @@ use Telegram\Bot\Exceptions\TelegramSDKException;
  */
 final class IM
 {
-    public static function getClient($type): Discord|Slack|Telegram
+    public static function getClient(int $type): Discord|Slack|Telegram
     {
         return match ($type) {
-            '1' => new Discord(),
-            '2' => new Slack(),
+            1 => new Discord(),
+            2 => new Slack(),
             default => new Telegram(),
         };
     }
@@ -28,7 +28,7 @@ final class IM
      * @throws GuzzleException
      * @throws TelegramSDKException
      */
-    public static function send($to, $msg, $type): void
+    public static function send(int $to, string $msg, int $type): void
     {
         self::getClient($type)->send($to, $msg);
     }

+ 1 - 1
src/Services/IM/Base.php

@@ -6,5 +6,5 @@ namespace App\Services\IM;
 
 abstract class Base
 {
-    abstract public function send($to, $msg): void;
+    abstract public function send(int $to, string $msg): void;
 }

+ 16 - 1
src/Services/IM/Discord.php

@@ -8,6 +8,7 @@ use App\Models\Config;
 use Exception;
 use GuzzleHttp\Client;
 use GuzzleHttp\Exception\GuzzleException;
+use function str_replace;
 use const VERSION;
 
 final class Discord extends Base
@@ -25,7 +26,7 @@ final class Discord extends Base
      * @throws GuzzleException
      * @throws Exception
      */
-    public function send($to, $msg): void
+    public function send(int $to, string $msg): void
     {
         $headers = [
             'Authorization' => "Bot {$this->token}",
@@ -56,6 +57,20 @@ final class Discord extends Base
 
         $channel_url = 'https://discord.com/api/v10/channels/' . $to . '/messages';
 
+        $msg = str_replace(
+            [
+                ';',
+                ']',
+                '\\',
+            ],
+            [
+                '\;',
+                '\]',
+                '\\\\',
+            ],
+            $msg
+        );
+
         $msg_body = [
             'content' => $msg,
         ];

+ 1 - 1
src/Services/IM/Slack.php

@@ -24,7 +24,7 @@ final class Slack extends Base
      * @throws GuzzleException
      * @throws Exception
      */
-    public function send($to, $msg): void
+    public function send(int $to, string $msg): void
     {
         $url = 'https://slack.com/api/chat.postMessage';
 

+ 15 - 27
src/Services/IM/Telegram.php

@@ -7,6 +7,7 @@ namespace App\Services\IM;
 use App\Models\Config;
 use Telegram\Bot\Api;
 use Telegram\Bot\Exceptions\TelegramSDKException;
+use function str_replace;
 use function strip_tags;
 
 final class Telegram extends Base
@@ -32,10 +33,20 @@ final class Telegram extends Base
             $to = Config::obtain('telegram_chatid');
         }
 
+        $msg = str_replace(
+            [
+                '_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!',
+            ],
+            [
+                '\_', '\*', '\[', '\]', '\(', '\)', '\~', '\`', '\>', '\#', '\+', '\-', '\=', '\|', '\{', '\}', '\.', '\!',
+            ],
+            $msg
+        );
+
         $sendMessage = [
             'chat_id' => $to,
             'text' => $msg,
-            'parse_mode' => '',
+            'parse_mode' => 'MarkdownV2',
             'disable_web_page_preview' => false,
             'reply_to_message_id' => null,
             'reply_markup' => null,
@@ -49,7 +60,7 @@ final class Telegram extends Base
      *
      * @throws TelegramSDKException
      */
-    public function sendHtml($to = 0, $msg = ''): void
+    public function sendHtml(int $to = 0, string $msg = ''): void
     {
         if ($to === 0) {
             $to = Config::obtain('telegram_chatid');
@@ -60,7 +71,7 @@ final class Telegram extends Base
             'text' => strip_tags(
                 $msg,
                 ['b', 'strong', 'i', 'em', 'u', 'ins', 's', 'strike','del', 'span','tg-spoiler', 'a', 'tg-emoji',
-                    'code', 'pre',
+                    'code', 'pre', 'blockquote',
                 ]
             ),
             'parse_mode' => 'HTML',
@@ -77,7 +88,7 @@ final class Telegram extends Base
      *
      * @throws TelegramSDKException
      */
-    public function sendMarkdown($to = 0, $msg = ''): void
+    public function sendMarkdown(int $to = 0, string $msg = ''): void
     {
         if ($to === 0) {
             $to = Config::obtain('telegram_chatid');
@@ -94,27 +105,4 @@ final class Telegram extends Base
 
         $this->bot->sendMessage($sendMessage);
     }
-
-    /**
-     * 以 MarkdownV2 格式发送讯息,默认给群组发送
-     *
-     * @throws TelegramSDKException
-     */
-    public function sendMarkdownV2($to = 0, $msg = ''): void
-    {
-        if ($to === 0) {
-            $to = Config::obtain('telegram_chatid');
-        }
-
-        $sendMessage = [
-            'chat_id' => $to,
-            'text' => $msg,
-            'parse_mode' => 'MarkdownV2',
-            'disable_web_page_preview' => false,
-            'reply_to_message_id' => null,
-            'reply_markup' => null,
-        ];
-
-        $this->bot->sendMessage($sendMessage);
-    }
 }

+ 5 - 5
src/Services/Notification.php

@@ -84,7 +84,7 @@ final class Notification
                     ]
                 );
             } else {
-                IM::send($user->im_value, $msg, $user->im_type);
+                IM::send((int) $user->im_value, $msg, $user->im_type);
             }
         }
     }
@@ -93,18 +93,18 @@ final class Notification
      * @throws GuzzleException
      * @throws TelegramSDKException
      */
-    public static function notifyUserGroup($msg = ''): void
+    public static function notifyUserGroup(string $msg = ''): void
     {
         if (Config::obtain('enable_telegram_group_notify')) {
-            IM::send(Config::obtain('telegram_chatid'), $msg, 0);
+            IM::send((int) Config::obtain('telegram_chatid'), $msg, 0);
         }
 
         if (Config::obtain('enable_discord_channel_notify')) {
-            IM::send(Config::obtain('discord_channel_id'), $msg, 1);
+            IM::send((int) Config::obtain('discord_channel_id'), $msg, 1);
         }
 
         if (Config::obtain('enable_slack_channel_notify')) {
-            IM::send(Config::obtain('slack_channel_id'), $msg, 2);
+            IM::send((int) Config::obtain('slack_channel_id'), $msg, 2);
         }
     }
 }

+ 1 - 0
src/Services/View.php

@@ -34,6 +34,7 @@ final class View
 
     public static function getTwig(): Environment
     {
+        $user = Auth::getUser();
         $loader = new FilesystemLoader(BASE_PATH . '/resources/views/' . self::getTheme($user) . '/');
 
         $twig = new Environment($loader, [

+ 0 - 1
tests/App/Services/ViewTest.php

@@ -5,7 +5,6 @@ declare(strict_types=1);
 namespace App\Services;
 
 use PHPUnit\Framework\TestCase;
-use App\Services\View;
 use App\Models\User;
 
 final class ViewTest extends TestCase

+ 0 - 1
tests/App/Utils/ClassHelperTest.php

@@ -5,7 +5,6 @@ declare(strict_types=1);
 namespace App\Utils;
 
 use PHPUnit\Framework\TestCase;
-use App\Utils\ClassHelper;
 
 final class ClassHelperTest extends TestCase
 {

+ 0 - 1
tests/App/Utils/CookieTest.php

@@ -5,7 +5,6 @@ declare(strict_types=1);
 namespace App\Utils;
 
 use PHPUnit\Framework\TestCase;
-use App\Utils\Cookie;
 
 final class CookieTest extends TestCase
 {

+ 0 - 1
tests/App/Utils/ToolsTest.php

@@ -4,7 +4,6 @@ declare(strict_types=1);
 
 namespace App\Utils;
 
-use MaxMind\Db\Reader\InvalidDatabaseException;
 use PHPUnit\Framework\TestCase;
 use function date_default_timezone_set;
 use function strlen;