Jelajahi Sumber

国际化,增加英文文言,支持更换语言包

luolongfei 3 tahun lalu
induk
melakukan
5492548270

+ 132 - 129
.env.example

@@ -1,129 +1,132 @@
-#####################################################################
-# 注意事项
-#
-# - 环境变量的格式为“键=值”,顶格写,注意等号两边不能有空格,值可以用单引号或者双引号引起来,不引也行(下面的特殊情况必须引起来)
-# - 因为环境变量中“#”代表注释,若密码中存在“#”字符的,一定要使用单引号将整个密码引起来,否则解析会在“#”字符前截止,如果密码中存在单双引号的,
-#    需要在单双引号前加“\”转义
-# - 配置多账户不可省略单引号,且多个账户和密码的格式必须是“<账户1>@<密码1>|<账户2>@<密码2>|<账户3>@<密码3>”,不要有空格,就算有程序也会给你干掉
-#    e.g. MULTIPLE_ACCOUNTS='<账户1>@<密码1>|<账户2>@<密码2>|<账户3>@<密码3>'
-#    注意不要省略“<>”符号,否则无法正确匹配
-# - 若你只有单个账户,只配置FREENOM_USERNAME和FREENOM_PASSWORD就够了
-# - 单账户和多账户的配置会被合并在一起读取并去重
-#####################################################################
-
-# .env 文件版本
-ENV_FILE_VERSION='v2'
-
-######################  账户配置 Account config  #########################
-# Freenom 账户 Freenom Account
[email protected]
-
-# Freenom 密码 Freenom password
-FREENOM_PASSWORD=''
-
-# 多账户支持 Support for multiple accounts
-MULTIPLE_ACCOUNTS=''
-
-# Freenom 代理 e.g. http://127.0.0.1:1081 or socks5://127.0.0.1:1080
-FREENOM_PROXY=''
-######################  end 账户配置  #########################
-
-######################  通知邮件配置 Email config  #########################
-# 机器人邮箱账户 Email of robot
[email protected]
-
-# 机器人邮箱密码(Gmail填密码,QQ邮箱或163邮箱填授权码) Password of the robot email
-MAIL_PASSWORD=''
-
-# 用于接收通知的邮箱 Email address used to receive notifications
-TO=''
-
-# 是否启用邮件推送功能 1:启用 0:不启用 Whether to enable email push features 1: enabled 0: not enabled
-MAIL_ENABLE=1
-
-# 自定义邮箱配置 Custom email config
-# 如果你想使用除 “QQ邮箱、163邮箱、Gmail、Outlook邮箱” 外的第三方邮箱或者自建邮箱服务作为机器人邮箱,可以自定义邮箱配置,否则请不要配置这些项
-# If you want to use third-party mailboxes or self-built mailbox services other than "QQ mailbox, 163 mailbox, Gmail, Outlook mailbox" as robot mailbox, you can customize mailbox configuration, otherwise, please don't configure these items.
-
-# SMTP 服务器 SMTP server
-MAIL_HOST=''
-
-# SMTP 端口号 SMTP port
-MAIL_PORT=''
-
-# 邮件加密方式 Email encryption method
-MAIL_ENCRYPTION=''
-
-# end 自定义邮箱配置 Custom email config
-
-######################  end 通知邮件配置  #########################
-
-######################  Telegram bot  #########################
-# 可选配置,通过 Telegram bot 发送通知消息 This is an optional configuration to send notification messages via Telegram bot
-
-# 你的chat_id,通过发送“/start”给@userinfobot可以获取自己的id Your chat_id, you can get your own id by sending "/start" to @userinfobot
-TELEGRAM_CHAT_ID=''
-
-# 你的Telegram bot的token Token for your Telegram bot
-TELEGRAM_BOT_TOKEN=''
-
-# Telegram 代理 e.g. http://127.0.0.1:1081 or socks5://127.0.0.1:1080
-TELEGRAM_PROXY=''
-
-# 是否启用 Telegram Bot 功能 1:启用 0:不启用 Whether to enable Telegram Bot features 1: enabled 0: not enabled
-TELEGRAM_BOT_ENABLE=0
-######################  end Telegram bot  #########################
-
-######################  企业微信  #########################
-# 企业 ID Corporate ID
-WECHAT_CORP_ID=''
-
-# 应用的凭证密钥 Credential keys for enterprise WeChat applications
-WECHAT_CORP_SECRET=''
-
-# 应用 ID
-WECHAT_AGENT_ID=''
-
-# 是否启用企业微信送信功能 1:启用 0:不启用 Whether to enable the enterprise WeChat message push function 1: Enable 0: Do not enable
-WECHAT_ENABLE=0
-######################  end 企业微信  #########################
-
-######################  Server 酱  #########################
-# Server 酱的 SendKey
-SCT_SEND_KEY=''
-
-# 是否启用 Server 酱 送信功能 1:启用 0:不启用 Whether to enable the ServerChan message function 1: enabled 0: not enabled
-SCT_ENABLE=0
-######################  end Server 酱  #########################
-
-######################  Bark 送信  #########################
-# Bark key 打开 Bark App,注册设备后看到的 key Open the Bark App, register the device and see the key
-# 支持直接粘贴 “https://api.day.app/xxx/这里改成你自己的推送内容” 这类文本,程序会自动从中提取有效的 Bark key Support direct paste "https://api.day.app/xxx/这里改成你自己的推送内容" type of text, the program will automatically extract the valid Bark key from it
-BARK_KEY=''
-
-# Bark 域名 默认是 Bark 作者提供的服务,建议自建 The Bark URL defaults to the server provided by the Bark author, and it is recommended to build your own
-BARK_URL='https://api.day.app'
-
-# Bark 其它相关参数(保持默认即可)
-BARK_IS_ARCHIVE=''
-BARK_GROUP='FreeNom'
-BARK_LEVEL='active'
-BARK_ICON='https://q2.qlogo.cn/headimg_dl?dst_uin=593198779&spec=100'
-BARK_JUMP_URL=''
-BARK_SOUND='gotosleep'
-
-# 是否启用 Bark 推送消息 1:启用 0:不启用 Whether to enable Bark push messaging 1: Enable 0: Do not enable
-BARK_ENABLE=0
-######################  end Bark 送信  #########################
-
-# 通知频率 0:仅当有续期操作的时候 1:每次执行 Notification frequency 0: Only when there is a renewal operation 1: Each execution
-NOTICE_FREQ=1
-
-# 是否验证服务器证书 1:验证 0:不验证 Whether to verify the server certificate 1: verify 0: no verification
-VERIFY_SSL=0
-
-# 是否开启 Debug 模式 1:开启 0:关闭 Whether to turn on Debug mode 1: On 0: Off
-DEBUG=0
-
-# 检测是否有新版本可用,发现新版本时推送消息通知 1:开启 0:关闭 Automatically detects if a new version is available and pushes a message notification when a new version is found 1: On 0: Off
-NEW_VERSION_DETECTION=1
+#####################################################################
+# 注意事项
+#
+# - 环境变量的格式为“键=值”,顶格写,注意等号两边不能有空格,值可以用单引号或者双引号引起来,不引也行(下面的特殊情况必须引起来)
+# - 因为环境变量中“#”代表注释,若密码中存在“#”字符的,一定要使用单引号将整个密码引起来,否则解析会在“#”字符前截止,如果密码中存在单双引号的,
+#    需要在单双引号前加“\”转义
+# - 配置多账户不可省略单引号,且多个账户和密码的格式必须是“<账户1>@<密码1>|<账户2>@<密码2>|<账户3>@<密码3>”,不要有空格,就算有程序也会给你干掉
+#    e.g. MULTIPLE_ACCOUNTS='<账户1>@<密码1>|<账户2>@<密码2>|<账户3>@<密码3>'
+#    注意不要省略“<>”符号,否则无法正确匹配
+# - 若你只有单个账户,只配置 FREENOM_USERNAME 和FREENOM_PASSWORD 就够了
+# - 单账户和多账户的配置会被合并在一起读取并去重
+#####################################################################
+
+# .env 文件版本
+ENV_FILE_VERSION='v2.1'
+
+######################  账户配置 Account config  #########################
+# Freenom 账户 Freenom Account
[email protected]
+
+# Freenom 密码 Freenom password
+FREENOM_PASSWORD=''
+
+# 多账户支持 Support for multiple accounts
+MULTIPLE_ACCOUNTS=''
+
+# Freenom 代理 e.g. http://127.0.0.1:1081 or socks5://127.0.0.1:1080
+FREENOM_PROXY=''
+######################  end 账户配置  #########################
+
+######################  通知邮件配置 Email config  #########################
+# 机器人邮箱账户 Email of robot
[email protected]
+
+# 机器人邮箱密码(Gmail填密码,QQ邮箱或163邮箱填授权码) Password of the robot email
+MAIL_PASSWORD=''
+
+# 用于接收通知的邮箱 Email address used to receive notifications
+TO=''
+
+# 是否启用邮件推送功能 1:启用 0:不启用 Whether to enable email push features 1: enabled 0: not enabled
+MAIL_ENABLE=1
+
+# 自定义邮箱配置 Custom email config
+# 如果你想使用除 “QQ邮箱、163邮箱、Gmail、Outlook邮箱” 外的第三方邮箱或者自建邮箱服务作为机器人邮箱,可以自定义邮箱配置,否则请不要配置这些项
+# If you want to use third-party mailboxes or self-built mailbox services other than "QQ mailbox, 163 mailbox, Gmail, Outlook mailbox" as robot mailbox, you can customize mailbox configuration, otherwise, please don't configure these items.
+
+# SMTP 服务器 SMTP server
+MAIL_HOST=''
+
+# SMTP 端口号 SMTP port
+MAIL_PORT=''
+
+# 邮件加密方式 Email encryption method
+MAIL_ENCRYPTION=''
+
+# end 自定义邮箱配置 Custom email config
+
+######################  end 通知邮件配置  #########################
+
+######################  Telegram bot  #########################
+# 可选配置,通过 Telegram bot 发送通知消息 This is an optional configuration to send notification messages via Telegram bot
+
+# 你的chat_id,通过发送“/start”给@userinfobot可以获取自己的id Your chat_id, you can get your own id by sending "/start" to @userinfobot
+TELEGRAM_CHAT_ID=''
+
+# 你的Telegram bot的token Token for your Telegram bot
+TELEGRAM_BOT_TOKEN=''
+
+# Telegram 代理 e.g. http://127.0.0.1:1081 or socks5://127.0.0.1:1080
+TELEGRAM_PROXY=''
+
+# 是否启用 Telegram Bot 功能 1:启用 0:不启用 Whether to enable Telegram Bot features 1: enabled 0: not enabled
+TELEGRAM_BOT_ENABLE=0
+######################  end Telegram bot  #########################
+
+######################  企业微信  #########################
+# 企业 ID Corporate ID
+WECHAT_CORP_ID=''
+
+# 应用的凭证密钥 Credential keys for enterprise WeChat applications
+WECHAT_CORP_SECRET=''
+
+# 应用 ID
+WECHAT_AGENT_ID=''
+
+# 是否启用企业微信送信功能 1:启用 0:不启用 Whether to enable the enterprise WeChat message push function 1: Enable 0: Do not enable
+WECHAT_ENABLE=0
+######################  end 企业微信  #########################
+
+######################  Server 酱  #########################
+# Server 酱的 SendKey
+SCT_SEND_KEY=''
+
+# 是否启用 Server 酱 送信功能 1:启用 0:不启用 Whether to enable the ServerChan message function 1: enabled 0: not enabled
+SCT_ENABLE=0
+######################  end Server 酱  #########################
+
+######################  Bark 送信  #########################
+# Bark key 打开 Bark App,注册设备后看到的 key Open the Bark App, register the device and see the key
+# 支持直接粘贴 “https://api.day.app/xxx/这里改成你自己的推送内容” 这类文本,程序会自动从中提取有效的 Bark key Support direct paste "https://api.day.app/xxx/这里改成你自己的推送内容" type of text, the program will automatically extract the valid Bark key from it
+BARK_KEY=''
+
+# Bark 域名 默认是 Bark 作者提供的服务,建议自建 The Bark URL defaults to the server provided by the Bark author, and it is recommended to build your own
+BARK_URL='https://api.day.app'
+
+# Bark 其它相关参数(保持默认即可)
+BARK_IS_ARCHIVE=''
+BARK_GROUP='FreeNom'
+BARK_LEVEL='active'
+BARK_ICON='https://q2.qlogo.cn/headimg_dl?dst_uin=593198779&spec=100'
+BARK_JUMP_URL=''
+BARK_SOUND='gotosleep'
+
+# 是否启用 Bark 推送消息 1:启用 0:不启用 Whether to enable Bark push messaging 1: Enable 0: Do not enable
+BARK_ENABLE=0
+######################  end Bark 送信  #########################
+
+# 通知频率 0:仅当有续期操作的时候 1:每次执行 Notification frequency 0: Only when there is a renewal operation 1: Each execution
+NOTICE_FREQ=1
+
+# 是否验证服务器证书 1:验证 0:不验证 Whether to verify the server certificate 1: verify 0: no verification
+VERIFY_SSL=0
+
+# 是否开启 Debug 模式 1:开启 0:关闭 Whether to turn on Debug mode 1: On 0: Off
+DEBUG=0
+
+# 检测是否有新版本可用,发现新版本时推送消息通知 1:开启 0:关闭 Automatically detects if a new version is available and pushes a message notification when a new version is found 1: On 0: Off
+NEW_VERSION_DETECTION=1
+
+# 应用语言配置 Application language configuration, the supported values are zh or en, zh means Chinese, en means English
+LANGUAGE=zh

+ 21 - 17
app/Console/FreeNom.php

@@ -18,7 +18,7 @@ use Luolongfei\Libs\Message;
 
 class FreeNom extends Base
 {
-    const VERSION = 'v0.4.4';
+    const VERSION = 'v0.4.5';
 
     const TIMEOUT = 33;
 
@@ -93,7 +93,7 @@ class FreeNom extends Base
             'proxy' => config('freenom_proxy'),
         ]);
 
-        system_log(sprintf('当前程序版本 %s', self::VERSION));
+        system_log(sprintf(lang('100038'), self::VERSION));
     }
 
     private function __clone()
@@ -128,7 +128,7 @@ class FreeNom extends Base
         }
 
         if (empty($this->jar->getCookieByName('WHMCSZH5eHTGhfvzP')->getValue())) {
-            throw new LlfException(34520002, lang('error_msg.100001'));
+            throw new LlfException(34520002, lang('100001'));
         }
 
         return true;
@@ -244,16 +244,16 @@ class FreeNom extends Base
                 'renewalFailuresArr' => $renewalFailuresArr,
                 'domainStatusArr' => $domainStatusArr,
             ];
-            $result = Message::send('', '主人,我刚刚帮你续期域名啦~', 2, $data);
+            $result = Message::send('', lang('100039'), 2, $data);
 
             system_log(sprintf(
-                '恭喜,成功续期 <green>%d</green> 个域名,失败 <green>%d</green> 个域名。%s',
+                lang('100040'),
                 count($renewalSuccessArr),
                 count($renewalFailuresArr),
-                $result ? '详细的续期结果已送信成功,请注意查收。' : ''
+                $result ? lang('100041') : ''
             ));
 
-            Log::info(sprintf("账户:%s\n续期结果如下:\n", $this->username), $data);
+            Log::info(sprintf(lang('100042'), $this->username), $data);
 
             return true;
         }
@@ -264,12 +264,12 @@ class FreeNom extends Base
                 'username' => $this->username,
                 'domainStatusArr' => $domainStatusArr,
             ];
-            Message::send('', '报告,今天没有域名需要续期', 3, $data);
+            Message::send('', lang('100043'), 3, $data);
         } else {
-            system_log('当前通知频率为「仅当有续期操作时」,故本次不会推送通知');
+            system_log(lang('100044'));
         }
 
-        system_log(sprintf('%s:<green>执行成功,今次没有需要续期的域名。</green>', $this->username));
+        system_log(sprintf(lang('100045'), $this->username));
 
         return true;
     }
@@ -303,7 +303,7 @@ class FreeNom extends Base
 
             return stripos($resp, 'Order Confirmation') !== false;
         } catch (\Exception $e) {
-            $errorMsg = sprintf('续期请求出错:%s,域名 ID:%s(账户:%s)', $e->getMessage(), $id, $this->username);
+            $errorMsg = sprintf(lang('100046'), $e->getMessage(), $id, $this->username);
             system_log($errorMsg);
             Message::send($errorMsg);
 
@@ -393,12 +393,12 @@ class FreeNom extends Base
     private function sendExceptionReport($e)
     {
         Message::send(sprintf(
-            '具体是在%s文件的第%d行,抛出了一个异常。异常的内容是%s,快去看看吧。(账户:%s)',
+            lang('100047'),
             $e->getFile(),
             $e->getLine(),
             $e->getMessage(),
             $this->username
-        ), '主人,出错了,' . $e->getMessage());
+        ), lang('100048') . $e->getMessage());
     }
 
     /**
@@ -408,11 +408,15 @@ class FreeNom extends Base
     public function handle()
     {
         $accounts = $this->getAccounts();
+        $totalAccounts = count($accounts);
 
-        system_log(sprintf('共发现 <green>%d</green> 个 freenom 账户,处理中', count($accounts)));
+        system_log(sprintf(lang('100049'), $totalAccounts));
 
-        foreach ($accounts as $account) {
+        foreach ($accounts as $index => $account) {
             try {
+                $num = $index + 1;
+                system_log(sprintf(lang('100050'), $num, $num, $totalAccounts));
+
                 $this->username = $account['username'];
                 $this->password = $account['password'];
 
@@ -425,10 +429,10 @@ class FreeNom extends Base
 
                 $this->renewAllDomains($allDomains, $token);
             } catch (LlfException $e) {
-                system_log(sprintf('出错:<red>%s</red>', $e->getMessage()));
+                system_log(sprintf(lang('100051'), $e->getMessage()));
                 $this->sendExceptionReport($e);
             } catch (\Exception $e) {
-                system_log(sprintf('出错:<red>%s</red>', $e->getMessage()), $e->getTrace());
+                system_log(sprintf(lang('100052'), $e->getMessage()), $e->getTrace());
                 $this->sendExceptionReport($e);
             }
         }

+ 10 - 10
app/Console/MigrateEnvFile.php

@@ -118,11 +118,11 @@ class MigrateEnvFile extends Base
         $file = $this->getEnvFilePath($filename);
 
         if (!file_exists($file)) {
-            throw new \Exception('文件不存在:' . $file);
+            throw new \Exception(lang('100021') . $file);
         }
 
         if (($fileContent = file_get_contents($file)) === false) {
-            throw new \Exception('读取文件内容失败:' . $file);
+            throw new \Exception(lang('100022') . $file);
         }
 
         if (!preg_match('/^ENV_FILE_VERSION=(?P<env_file_version>.*?)$/im', $fileContent, $m)) {
@@ -143,7 +143,7 @@ class MigrateEnvFile extends Base
     public function backup()
     {
         if (copy($this->getEnvFilePath(), $this->getEnvFilePath('.env.old')) === false) {
-            throw new \Exception('备份 .env 文件到 .env.old 文件时出错');
+            throw new \Exception(lang('100020'));
         }
 
         return true;
@@ -158,7 +158,7 @@ class MigrateEnvFile extends Base
     public function genNewEnvFile()
     {
         if (copy($this->getEnvFilePath('.env.example'), $this->getEnvFilePath('.env')) === false) {
-            throw new \Exception('从 .env.example 文件生成 .env 文件时出错');
+            throw new \Exception(lang('100019'));
         }
 
         return true;
@@ -239,22 +239,22 @@ class MigrateEnvFile extends Base
                 return true;
             }
 
-            system_log('检测到你的 .env 文件内容过旧,程式将根据 .env.example 文件自动更新相关配置项,不要慌张,此操作对已有数据不会有任何影响');
+            system_log(lang('100013'));
 
             $this->backup();
-            system_log(sprintf('<green>已完成 .env 文件备份</green>,旧文件位置为 %s/.env.old', ROOT_PATH));
+            system_log(sprintf(lang('100014'), ROOT_PATH));
 
             $this->genNewEnvFile();
-            system_log('已生成新 .env 文件');
+            system_log(lang('100015'));
 
             $this->migrateData($this->allOldEnvValues);
-            system_log(sprintf('<green>数据迁移完成</green>,共迁移 %d 条环境变量数据', $this->migrateNum));
+            system_log(sprintf(lang('100016'), $this->migrateNum));
 
-            system_log('<green>恭喜,已成功完成 .env 文件升级</green>');
+            system_log(lang('100017'));
 
             return true;
         } catch (\Exception $e) {
-            system_log('升级 .env 文件出错:' . $e->getMessage());
+            system_log(lang('100018') . $e->getMessage());
 
             return false;
         }

+ 29 - 18
app/Console/Upgrade.php

@@ -58,6 +58,8 @@ class Upgrade extends Base
     {
         $this->pushedVerFile = DATA_PATH . DS . 'pushed_version.txt';
 
+        $this->language = env('LANGUAGE', 'zh');
+
         $this->client = new Client([
             'base_uri' => 'https://api.github.com',
             'headers' => [
@@ -94,7 +96,7 @@ class Upgrade extends Base
                 || !isset($resp['name'])
                 || !isset($resp['published_at'])
                 || !isset($resp['html_url'])) {
-                throw new \Exception('Github 返回的数据与预期不一致:' . json_encode($resp, JSON_UNESCAPED_UNICODE));
+                throw new \Exception(lang('100023') . json_encode($resp, JSON_UNESCAPED_UNICODE));
             }
 
             $this->releaseInfo = $resp;
@@ -104,7 +106,7 @@ class Upgrade extends Base
 
             return version_compare($this->latestVer, $this->currVer, '>');
         } catch (\Exception $e) {
-            Log::error('检测升级出错:' . $e->getMessage());
+            Log::error(lang('100024') . $e->getMessage());
 
             return false;
         }
@@ -147,18 +149,27 @@ class Upgrade extends Base
      */
     public function genMsgContent()
     {
-        $content = sprintf(
-            "见信好,我们在 %s 发布了新版 FreeNom 续期工具 v%s,而你当前正在使用的版本为 v%s,你可以根据自己的实际需要决定是否升级到新版本。今次新版有以下更新或改进:\n\n",
-            $this->friendlyDateFormat($this->releaseInfo['published_at'], 'UTC'),
-            $this->latestVer,
-            $this->currVer
-        );
+        if ($this->language === 'zh') {
+            $content = sprintf(
+                lang('100025'),
+                $this->friendlyDateFormat($this->releaseInfo['published_at'], 'UTC'),
+                $this->latestVer,
+                $this->currVer
+            );
+        } else {
+            $content = sprintf(
+                lang('100025'),
+                $this->latestVer,
+                $this->friendlyDateFormat($this->releaseInfo['published_at'], 'UTC'),
+                $this->currVer
+            );
+        }
 
         $content .= $this->releaseInfo['body'];
 
-        $content .= "\n\n" . '欲知更多信息,请访问:' . $this->releaseInfo['html_url'];
+        $content .= "\n\n" . lang('100026') . $this->releaseInfo['html_url'];
 
-        $content .= "\n\n" . '(本消息针对同一个新版只会推送一次,如果你不想收到新版本通知,将 .env 文件中的 NEW_VERSION_DETECTION 的值设为 0 即可)';
+        $content .= "\n\n" . lang('100027');
 
         return $content;
     }
@@ -181,15 +192,15 @@ class Upgrade extends Base
 
             if ($diff < 86400) {
                 if ($d->format('d') === date('d')) {
-                    return $diff < 60 ? '刚刚' : ($diff < 3600 ? floor($diff / 60) . '分钟前' : floor($diff / 3600) . '小时前');
+                    return $diff < 60 ? lang('100028') : ($diff < 3600 ? floor($diff / 60) . lang('100029') : floor($diff / 3600) . lang('100030'));
                 } else {
-                    return '昨天 ' . $d->format('H:i');
+                    return lang('100031') . $d->format('H:i');
                 }
             } else {
                 return $d->format($d->format('Y') === date('Y') ? 'Y-m-d H:i' : 'Y-m-d');
             }
         } catch (\Exception $e) {
-            Log::error('转人类友好时间出错:' . $e->getMessage());
+            Log::error(lang('100032') . $e->getMessage());
 
             return $date;
         }
@@ -211,34 +222,34 @@ class Upgrade extends Base
 
             if (IS_SCF) {
                 system_log(sprintf(
-                    'FreeNom 续期工具有新的版本可用,你当前版本为 v%s,最新版本为 v%s。关于新版的详细信息,请访问:%s',
+                    lang('100033'),
                     $this->currVer,
                     $this->latestVer,
                     $this->releaseInfo['html_url']
                 ));
             } else {
                 system_log(sprintf(
-                    '<green>FreeNom 续期工具有新的版本可用,最新版本为 v%s(%s)</green>',
+                    lang('100034'),
                     $this->latestVer,
                     $this->releaseInfo['html_url']
                 ));
 
                 $result = Message::send(
                     $this->genMsgContent(),
-                    sprintf('主人,FreeNom 续期工具有新的版本(v%s)可用,新版相关情况已给到你', $this->latestVer),
+                    sprintf(lang('100035'), $this->latestVer),
                     4,
                     $this->releaseInfo
                 );
 
                 if ($result) {
                     $this->rememberVer($this->latestVer);
-                    system_log('有关新版的信息已送信给到你,请注意查收。');
+                    system_log(lang('100036'));
                 }
             }
 
             return true;
         } catch (\Exception $e) {
-            system_log('升级出错:' . $e->getMessage());
+            system_log(lang('100037') . $e->getMessage());
 
             return false;
         }

+ 4 - 4
app/helpers.php

@@ -236,9 +236,9 @@ if (!function_exists('system_check')) {
 
         // 如果是在云函数部署,则不需要检查这几项
         if (IS_SCF) {
-            system_log('检测到运行环境为云函数,所有环境变量将直接从环境中读取,环境中找不到的变量,则直接从 .env.example 文件中读取');
-            system_log('如果是在腾讯云函数,可以参考此处修改或新增环境变量,无需重建:https://github.com/luolongfei/freenom/blob/main/resources/screenshot/scf03.png');
-            system_log('如果是在阿里云函数,可以直接在【函数详情】->【函数配置】->【环境信息】处编辑环境变量');
+            system_log(lang('100009'));
+            system_log(lang('100010'));
+            system_log(lang('100011'));
         } else {
             if (!function_exists('putenv')) {
                 throw new LlfException(34520005);
@@ -257,7 +257,7 @@ if (!function_exists('system_check')) {
         if (config('new_version_detection')) {
             Upgrade::getInstance()->handle();
         } else {
-            system_log('由于你没有开启升级提醒功能,故无法在有新版本可用时第一时间收到通知。将 .env 文件中 NEW_VERSION_DETECTION 的值改为 1 即可重新开启相关功能。');
+            system_log(lang('100012'));
         }
 
         if (!extension_loaded('curl')) {

+ 5 - 20
config.php

@@ -22,18 +22,14 @@ return [
             'username' => env('MAIL_USERNAME'), // 机器人邮箱账户
             'password' => env('MAIL_PASSWORD'), // 机器人邮箱密码或授权码
             'enable' => (int)env('MAIL_ENABLE'), // 是否启用,默认启用
-
             'not_enabled_tips' => env('MAIL_USERNAME') && env('MAIL_PASSWORD'), // 提醒未启用
-
             // 'reply_to' => '[email protected]', // 接收回复的邮箱
             // 'reply_to_name' => '作者', // 接收回复的人名
-
             'host' => env('MAIL_HOST'), // 邮件 SMTP 服务器
             'port' => env('MAIL_PORT'), // 邮件 SMTP 端口
             'encryption' => env('MAIL_ENCRYPTION'), // 邮件加密方式
-
             'class' => \Luolongfei\Libs\MessageServices\Mail::class,
-            'name' => '邮件',
+            'name' => lang('100064'),
         ],
 
         /**
@@ -43,12 +39,9 @@ return [
             'chat_id' => env('TELEGRAM_CHAT_ID'), // 你的chat_id,通过发送“/start”给@userinfobot可以获取自己的id
             'token' => env('TELEGRAM_BOT_TOKEN'), // Telegram Bot 的 token
             'enable' => (int)env('TELEGRAM_BOT_ENABLE'), // 是否启用,默认不启用
-
             'not_enabled_tips' => env('TELEGRAM_CHAT_ID') && env('TELEGRAM_BOT_TOKEN'), // 提醒未启用
-
             'class' => \Luolongfei\Libs\MessageServices\TelegramBot::class,
-            'name' => 'Telegram Bot',
-
+            'name' => lang('100065'),
             'proxy' => env('TELEGRAM_PROXY'),
         ],
 
@@ -60,11 +53,9 @@ return [
             'corp_secret' => env('WECHAT_CORP_SECRET'), // 企业微信应用的凭证密钥
             'agent_id' => (int)env('WECHAT_AGENT_ID'), // 企业微信应用 ID
             'enable' => (int)env('WECHAT_ENABLE'), // 是否启用,默认不启用
-
             'not_enabled_tips' => env('WECHAT_CORP_ID') && env('WECHAT_CORP_SECRET') && env('WECHAT_AGENT_ID'), // 提醒未启用
-
             'class' => \Luolongfei\Libs\MessageServices\WeChat::class,
-            'name' => '企业微信',
+            'name' => lang('100066'),
         ],
 
         /**
@@ -73,11 +64,9 @@ return [
         'sct' => [
             'sct_send_key' => env('SCT_SEND_KEY'), // SendKey
             'enable' => (int)env('SCT_ENABLE'), // 是否启用,默认不启用
-
             'not_enabled_tips' => (bool)env('SCT_SEND_KEY'), // 提醒未启用
-
             'class' => \Luolongfei\Libs\MessageServices\ServerChan::class,
-            'name' => 'Server 酱',
+            'name' => lang('100067'),
         ],
 
         /**
@@ -93,15 +82,11 @@ return [
             'bark_jump_url' => env('BARK_JUMP_URL') === '' ? null : env('BARK_JUMP_URL'),
             'bark_sound' => env('BARK_SOUND') === '' ? null : env('BARK_SOUND'),
             'enable' => (int)env('BARK_ENABLE'), // 是否启用,默认不启用
-
             'not_enabled_tips' => env('BARK_KEY') && env('BARK_URL'), // 提醒未启用
-
             'class' => \Luolongfei\Libs\MessageServices\Bark::class,
-            'name' => 'Bark',
+            'name' => lang('100068'),
         ],
     ],
-
-    'locale' => 'zh', // 指定语言包,位于resources/lang/目录下
     'notice_freq' => (int)env('NOTICE_FREQ', 1), // 通知频率 0:仅当有续期操作的时候 1:每次执行
     'verify_ssl' => (bool)env('VERIFY_SSL', 0), // 请求时验证 SSL 证书行为,默认不验证,防止服务器证书过期或证书颁布者信息不全导致无法发出请求
     'debug' => (bool)env('DEBUG'),

+ 9 - 9
index.php

@@ -49,8 +49,8 @@ use Luolongfei\Libs\Message;
 function customize_error_handler()
 {
     if (!is_null($error = error_get_last())) {
-        Log::error('程序意外终止', $error);
-        Message::send('可能存在错误,这边收集到的错误信息为:' . json_encode($error, JSON_UNESCAPED_UNICODE), '主人,程序意外终止');
+        Log::error(lang('100057'), $error);
+        Message::send(lang('100058') . json_encode($error, JSON_UNESCAPED_UNICODE), lang('100059'));
     }
 }
 
@@ -61,8 +61,8 @@ function customize_error_handler()
  */
 function exception_handler($e)
 {
-    Log::error('未捕获的异常:' . $e->getMessage());
-    Message::send("具体的异常内容是:\n" . $e->getMessage(), '主人,未捕获的异常');
+    Log::error(lang('100060') . $e->getMessage());
+    Message::send(lang('100061') . $e->getMessage(), lang('100062'));
 }
 
 /**
@@ -89,7 +89,7 @@ function main_handler($event, $context)
 function handler($event, $context)
 {
     $logger = $GLOBALS['fcLogger'];
-    $logger->info('开始执行阿里云函数');
+    $logger->info(lang('100063'));
 
     return run();
 }
@@ -107,11 +107,11 @@ function run()
 
         $class::getInstance()->$fn();
 
-        return IS_SCF ? '云函数执行成功。' : true;
+        return IS_SCF ? lang('100007') : true;
     } catch (\Exception $e) {
-        system_log(sprintf('执行出错:<red>%s</red>', $e->getMessage()), $e->getTrace());
-        Message::send("执行出错:\n" . $e->getMessage(), '主人,捕获异常');
+        system_log(sprintf(lang('100006'), $e->getMessage()), $e->getTrace());
+        Message::send(lang('100004') . $e->getMessage(), lang('100005'));
     }
 
-    return IS_SCF ? '云函数执行失败。' : false;
+    return IS_SCF ? lang('100008') : false;
 }

+ 3 - 3
libs/Argv.php

@@ -24,9 +24,9 @@ class Argv extends Base
             $desc = <<<FLL
 Description
 Params:
--c: <string> 指定要实例化的类名。默认调用FreeNom类
--m: <string> 指定要调用的方法名(不支持静态方法)。默认调用handle方法
--h: 显示说明
+-c: <string> 指定要实例化的类名。默认调用 FreeNom  Specifies the name of the class to instantiate. The default call to the FreeNom class
+-m: <string> 指定要调用的方法名(不支持静态方法)。默认调用 handle 方法 Specifies the name of the method to be called (static methods are not supported). The handle method is called by default
+-h: 显示说明 Help
 
 Example: 
 $ php run -c=FreeNom -m=handle

+ 2 - 2
libs/Base.php

@@ -25,11 +25,11 @@ class Base
     public static function addInstance(string $className, bool $overwrite = false)
     {
         if (isset(self::$instances[$className]) && !$overwrite) {
-            throw new \InvalidArgumentException(sprintf('类 %s 的实例已存在', $className));
+            throw new \InvalidArgumentException(sprintf(lang('100053'), $className));
         }
 
         if (!class_exists($className)) {
-            throw new \Exception(sprintf('类 %s 不存在', $className));
+            throw new \Exception(sprintf(lang('100054'), $className));
         }
 
         $instance = new $className();

+ 1 - 1
libs/Connector/MessageGateway.php

@@ -35,7 +35,7 @@ abstract class MessageGateway implements MessageServiceInterface
     public function check(string $content, array $data)
     {
         if ($content === '' && empty($data)) {
-            throw new \Exception(lang('error_msg.100002'));
+            throw new \Exception(lang('100002'));
         }
     }
 

+ 4 - 1
libs/Lang.php

@@ -18,7 +18,8 @@ class Lang extends Base
 
     public function init()
     {
-        $this->lang = require sprintf('%s/lang/%s.php', RESOURCES_PATH, config('locale'));
+        // 读取语言包,位于 resources/lang/ 目录下
+        $this->lang = require sprintf('%s/lang/%s.php', RESOURCES_PATH, env('LANGUAGE', 'zh'));
     }
 
     /**
@@ -46,6 +47,8 @@ class Lang extends Base
             } else {
                 if (isset($lang[$key])) {
                     return $lang[$key];
+                } else if (isset($lang['messages'][$key])) { // 如果没有在根节点找到语言数据,则尝试从 messages 下标继续找寻
+                    return $lang['messages'][$key];
                 }
 
                 return null;

+ 2 - 2
libs/Message.php

@@ -35,7 +35,7 @@ abstract class Message extends Base
         foreach (config('message') as $conf) {
             if ($conf['enable'] !== 1) {
                 if ($conf['not_enabled_tips'] && self::$notEnabledTips) { // 仅在存在配置的送信项未启用的情况下提醒
-                    system_log(sprintf('由于没有启用「%s」功能,故本次不通过「%s」送信,尽管检测到相关配置。', $conf['name'], $conf['name']));
+                    system_log(sprintf(lang('100055'), $conf['name'], $conf['name']));
                 }
 
                 continue;
@@ -44,7 +44,7 @@ abstract class Message extends Base
             $serviceInstance = self::getInstance($conf['class'], 'IS_MESSAGE_SERVICE');
 
             if (!$serviceInstance instanceof MessageServiceInterface) {
-                throw new \Exception(sprintf('消息服务类 %s 必须继承并实现 MessageServiceInterface 接口', $conf['class']));
+                throw new \Exception(sprintf(lang('100056'), $conf['class']));
             }
 
             if ($serviceInstance->$method(...$params) && !$result) { // 任一方式送信成功即为成功

+ 13 - 13
libs/MessageServices/Bark.php

@@ -146,8 +146,8 @@ class Bark extends MessageGateway
     {
         $footer = '';
 
-        $footer .= "\n更多信息可以参考:https://my.freenom.com/domains.php?a=renewals(点击“复制内容”即可复制此网址)";
-        $footer .= "\n\n(如果你不想每次执行都收到推送,请将 .env 中 NOTICE_FREQ 的值设为 0,使程序只在有续期操作时才推送)";
+        $footer .= lang('100078');
+        $footer .= lang('100079');
 
         return $footer;
     }
@@ -162,16 +162,16 @@ class Bark extends MessageGateway
     public function genDomainStatusText(array $domainStatus)
     {
         if (empty($domainStatus)) {
-            return "无数据。\n";
+            return lang('100080');
         }
 
         $domainStatusText = '';
 
         foreach ($domainStatus as $domain => $daysLeft) {
-            $domainStatusText .= sprintf('%s 还有 %d 天到期,', $domain, $daysLeft);
+            $domainStatusText .= sprintf(lang('100081'), $domain, $daysLeft);
         }
 
-        $domainStatusText = rtrim($domainStatusText, ',') . "。\n";
+        $domainStatusText = rtrim(rtrim($domainStatusText, ' '), ',,') . lang('100082');
 
         return $domainStatusText;
     }
@@ -188,19 +188,19 @@ class Bark extends MessageGateway
      */
     public function genDomainRenewalResultsText(string $username, array $renewalSuccessArr, array $renewalFailuresArr, array $domainStatus)
     {
-        $text = sprintf("账户 %s 这次续期的结果如下\n\n", $username);
+        $text = sprintf(lang('100083'), $username);
 
         if ($renewalSuccessArr) {
-            $text .= '续期成功:';
+            $text .= lang('100084');
             $text .= $this->genDomainsText($renewalSuccessArr);
         }
 
         if ($renewalFailuresArr) {
-            $text .= '续期出错:';
+            $text .= lang('100085');
             $text .= $this->genDomainsText($renewalFailuresArr);
         }
 
-        $text .= "\n今次无需续期的域名及其剩余天数如下所示:\n\n";
+        $text .= lang('100086');
         $text .= $this->genDomainStatusText($domainStatus);
 
         $text .= $this->getFooter();
@@ -218,7 +218,7 @@ class Bark extends MessageGateway
      */
     public function genDomainStatusFullText(string $username, array $domainStatus)
     {
-        $markDownText = sprintf("我刚刚帮小主看了一下,账户 %s 今天并没有需要续期的域名。所有域名情况如下:\n\n", $username);
+        $markDownText = sprintf(lang('100087'), $username);
 
         $markDownText .= $this->genDomainStatusText($domainStatus);
 
@@ -251,7 +251,7 @@ class Bark extends MessageGateway
         } else if ($type === 3) {
             $content = $this->genDomainStatusFullText($data['username'], $data['domainStatusArr']);
         } else {
-            throw new \Exception(lang('error_msg.100003'));
+            throw new \Exception(lang('100003'));
         }
 
         $query = [
@@ -302,9 +302,9 @@ class Bark extends MessageGateway
                 return true;
             }
 
-            throw new \Exception($resp['message'] ?? '未知原因');
+            throw new \Exception($resp['message'] ?? lang('100088'));
         } catch (\Exception $e) {
-            system_log('Bark 送信失败:<red>' . $e->getMessage() . '</red>');
+            system_log(sprintf(lang('100089'), $e->getMessage()));
 
             return false;
         }

+ 21 - 16
libs/MessageServices/Mail.php

@@ -27,6 +27,12 @@ class Mail extends MessageGateway
      */
     public function __construct()
     {
+        $this->language = env('LANGUAGE', 'zh');
+
+        $this->noticeTemplatePath = sprintf('%s/mail/%s/notice.html', RESOURCES_PATH, $this->language);
+        $this->successfulRenewalTemplatePath = sprintf('%s/mail/%s/successful_renewal.html', RESOURCES_PATH, $this->language);
+        $this->noRenewalRequiredTemplatePath = sprintf('%s/mail/%s/no_renewal_required.html', RESOURCES_PATH, $this->language);
+
         $this->phpMailerInstance = new PHPMailer(true);
 
         $this->init();
@@ -92,8 +98,7 @@ class Mail extends MessageGateway
             $secure = config('message.mail.encryption');
             $port = (int)config('message.mail.port');
             if (!($host && $secure && $port)) {
-                throw new MailException('目前支持Gmail、QQ邮箱、163邮箱以及Outlook邮箱自动识别配置,其它类型的邮箱或者自建邮箱,'
-                    . '请在 .env 文件中追加“自定义邮箱配置”的所有相关项,否则无法使用邮件服务。');
+                throw new MailException(lang('100069'));
             }
         }
 
@@ -128,16 +133,16 @@ class Mail extends MessageGateway
     public function genDomainStatusHtml(array $domainStatus)
     {
         if (empty($domainStatus)) {
-            return "无数据。";
+            return lang('100070');
         }
 
         $domainStatusHtml = '';
 
         foreach ($domainStatus as $domain => $daysLeft) {
-            $domainStatusHtml .= sprintf('<a href="http://%s" rel="noopener" target="_blank">%s</a>还有 <span style="font-weight: bold; font-size: 16px;">%d</span> 天到期,', $domain, $domain, $daysLeft);
+            $domainStatusHtml .= sprintf(lang('100071'), $domain, $domain, $daysLeft);
         }
 
-        $domainStatusHtml = rtrim($domainStatusHtml, ',') . "。";
+        $domainStatusHtml = rtrim(rtrim($domainStatusHtml, ' '), ',,') . lang('100072');
 
         return $domainStatusHtml;
     }
@@ -166,8 +171,8 @@ class Mail extends MessageGateway
 
         $this->check($content, $data);
 
-        $this->phpMailerInstance->addAddress($recipient, config('message.mail.recipient_name', '主人')); // 添加收件人,参数2选填
-        $this->phpMailerInstance->addReplyTo(config('message.mail.reply_to', '[email protected]'), config('message.mail.reply_to_name', '作者')); // 备用回复地址,收到的回复的邮件将被发到此地址
+        $this->phpMailerInstance->addAddress($recipient, config('message.mail.recipient_name', lang('100073'))); // 添加收件人,参数2选填
+        $this->phpMailerInstance->addReplyTo(config('message.mail.reply_to', '[email protected]'), config('message.mail.reply_to_name', lang('100074'))); // 备用回复地址,收到的回复的邮件将被发到此地址
 
         /**
          * 抄送和密送都是添加收件人,抄送方式下,被抄送者知道除被密送者外的所有的收件人,密送方式下,
@@ -195,32 +200,32 @@ class Mail extends MessageGateway
          * $this->phpMailerInstance->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.'; // 纯文本消息正文。不支持html预览的邮件客户端将显示此预览消息,其它情况将显示正常的body
          */
         if ($type === 1) {
-            $template = file_get_contents(RESOURCES_PATH . '/mail/notice.html');
+            $template = file_get_contents($this->noticeTemplatePath);
             $message = sprintf($template, $content);
         } else if ($type === 2) {
-            $template = file_get_contents(RESOURCES_PATH . '/mail/successful_renewal.html');
+            $template = file_get_contents($this->successfulRenewalTemplatePath);
             $realData = [
                 $data['username'],
-                $data['renewalSuccessArr'] ? sprintf('续期成功:%s<br>', $this->genDomainsHtml($data['renewalSuccessArr'])) : '',
-                $data['renewalFailuresArr'] ? sprintf('续期出错:%s<br>', $this->genDomainsHtml($data['renewalFailuresArr'])) : '',
+                $data['renewalSuccessArr'] ? sprintf(lang('100075'), $this->genDomainsHtml($data['renewalSuccessArr'])) : '',
+                $data['renewalFailuresArr'] ? sprintf(lang('100076'), $this->genDomainsHtml($data['renewalFailuresArr'])) : '',
                 $this->genDomainStatusHtml($data['domainStatusArr'])
             ];
             $message = $this->genMessageContent($realData, $template);
         } else if ($type === 3) {
-            $template = file_get_contents(RESOURCES_PATH . '/mail/no_renewal_required.html');
+            $template = file_get_contents($this->noRenewalRequiredTemplatePath);
             $realData = [
                 $data['username'],
                 $this->genDomainStatusHtml($data['domainStatusArr'])
             ];
             $message = $this->genMessageContent($realData, $template);
         } else if ($type === 4) {
-            $template = file_get_contents(RESOURCES_PATH . '/mail/notice.html');
+            $template = file_get_contents($this->noticeTemplatePath);
             $message = sprintf($template, $this->newLine2Br($content));
         } else {
-            throw new \Exception(lang('error_msg.100003'));
+            throw new \Exception(lang('100003'));
         }
 
-        $this->phpMailerInstance->msgHTML($message, APP_PATH . '/mail');
+        $this->phpMailerInstance->msgHTML($message, RESOURCES_PATH . '/mail/' . $this->language);
 
         try {
             if (!$this->phpMailerInstance->send()) {
@@ -229,7 +234,7 @@ class Mail extends MessageGateway
 
             return true;
         } catch (\Exception $e) {
-            system_log('邮件发送失败:' . $e->getMessage());
+            system_log(lang('100077') . $e->getMessage());
 
             return false;
         }

+ 13 - 13
libs/MessageServices/ServerChan.php

@@ -48,7 +48,7 @@ class ServerChan extends MessageGateway
      */
     public function genDomainStatusFullMarkDownText(string $username, array $domainStatus)
     {
-        $markDownText = sprintf("我刚刚帮小主看了一下,账户 [%s](#) 今天并没有需要续期的域名。所有域名情况如下:\n\n", $username);
+        $markDownText = sprintf(lang('100090'), $username);
 
         $markDownText .= $this->genDomainStatusMarkDownText($domainStatus);
 
@@ -66,8 +66,8 @@ class ServerChan extends MessageGateway
     {
         $footer = '';
 
-        $footer .= "\n更多信息可以参考 [Freenom官网](https://my.freenom.com/domains.php?a=renewals) 哦~";
-        $footer .= "\n\n(如果你不想每次执行都收到推送,请将 .env 中 NOTICE_FREQ 的值设为 0,使程序只在有续期操作时才推送)";
+        $footer .= lang('100091');
+        $footer .= lang('100092');
 
         return $footer;
     }
@@ -82,16 +82,16 @@ class ServerChan extends MessageGateway
     public function genDomainStatusMarkDownText(array $domainStatus)
     {
         if (empty($domainStatus)) {
-            return "无数据。\n";
+            return lang('100093');
         }
 
         $domainStatusMarkDownText = '';
 
         foreach ($domainStatus as $domain => $daysLeft) {
-            $domainStatusMarkDownText .= sprintf('[%s](http://%s) 还有 *%d* 天到期,', $domain, $domain, $daysLeft);
+            $domainStatusMarkDownText .= sprintf(lang('100094'), $domain, $domain, $daysLeft);
         }
 
-        $domainStatusMarkDownText = rtrim($domainStatusMarkDownText, ',') . "。\n";
+        $domainStatusMarkDownText = rtrim(rtrim($domainStatusMarkDownText, ' '), ',,') . lang('100095');
 
         return $domainStatusMarkDownText;
     }
@@ -108,19 +108,19 @@ class ServerChan extends MessageGateway
      */
     public function genDomainRenewalResultsMarkDownText(string $username, array $renewalSuccessArr, array $renewalFailuresArr, array $domainStatus)
     {
-        $text = sprintf("账户 [%s](#) 这次续期的结果如下\n\n", $username);
+        $text = sprintf(lang('100096'), $username);
 
         if ($renewalSuccessArr) {
-            $text .= '续期成功:';
+            $text .= lang('100097');
             $text .= $this->genDomainsMarkDownText($renewalSuccessArr);
         }
 
         if ($renewalFailuresArr) {
-            $text .= '续期出错:';
+            $text .= lang('100098');
             $text .= $this->genDomainsMarkDownText($renewalFailuresArr);
         }
 
-        $text .= "\n今次无需续期的域名及其剩余天数如下所示:\n\n";
+        $text .= lang('100099');
         $text .= $this->genDomainStatusMarkDownText($domainStatus);
 
         $text .= $this->getMarkDownFooter();
@@ -172,7 +172,7 @@ class ServerChan extends MessageGateway
         } else if ($type === 3) {
             $content = $this->genDomainStatusFullMarkDownText($data['username'], $data['domainStatusArr']);
         } else {
-            throw new \Exception(lang('error_msg.100003'));
+            throw new \Exception(lang('100003'));
         }
 
         $subject = $subject === '' ? mb_substr($content, 0, 12) . '...' : $subject;
@@ -194,9 +194,9 @@ class ServerChan extends MessageGateway
                 return true;
             }
 
-            throw new \Exception($resp['message'] ?? '未知原因');
+            throw new \Exception($resp['message'] ?? lang('100100'));
         } catch (\Exception $e) {
-            system_log('Server酱 消息发送失败:<red>' . $e->getMessage() . '</red>');
+            system_log(sprintf(lang('100101'), $e->getMessage()));
 
             return false;
         }

+ 12 - 12
libs/MessageServices/TelegramBot.php

@@ -59,7 +59,7 @@ class TelegramBot extends MessageGateway
      */
     public function genDomainStatusFullMarkDownText(string $username, array $domainStatus)
     {
-        $markDownText = sprintf("我刚刚帮小主看了一下,账户 %s 今天并没有需要续期的域名。所有域名情况如下:\n\n", $username);
+        $markDownText = sprintf(lang('100102'), $username);
 
         $markDownText .= $this->genDomainStatusMarkDownText($domainStatus);
 
@@ -77,8 +77,8 @@ class TelegramBot extends MessageGateway
     {
         $footer = '';
 
-        $footer .= "\n更多信息可以参考 [Freenom官网](https://my.freenom.com/domains.php?a=renewals) 哦~";
-        $footer .= "\n\n(如果你不想每次执行都收到推送,请将 .env 中 NOTICE_FREQ 的值设为 0,使程序只在有续期操作时才推送)";
+        $footer .= lang('100103');
+        $footer .= lang('100104');
 
         return $footer;
     }
@@ -93,16 +93,16 @@ class TelegramBot extends MessageGateway
     public function genDomainStatusMarkDownText(array $domainStatus)
     {
         if (empty($domainStatus)) {
-            return "无数据。\n";
+            return lang('100105');
         }
 
         $domainStatusMarkDownText = '';
 
         foreach ($domainStatus as $domain => $daysLeft) {
-            $domainStatusMarkDownText .= sprintf('[%s](http://%s) 还有 *%d* 天到期,', $domain, $domain, $daysLeft);
+            $domainStatusMarkDownText .= sprintf(lang('100106'), $domain, $domain, $daysLeft);
         }
 
-        $domainStatusMarkDownText = rtrim($domainStatusMarkDownText, ',') . "。\n";
+        $domainStatusMarkDownText = rtrim(rtrim($domainStatusMarkDownText, ' '), ',,') . lang('100107');
 
         return $domainStatusMarkDownText;
     }
@@ -119,19 +119,19 @@ class TelegramBot extends MessageGateway
      */
     public function genDomainRenewalResultsMarkDownText(string $username, array $renewalSuccessArr, array $renewalFailuresArr, array $domainStatus)
     {
-        $text = sprintf("账户 %s 这次续期的结果如下\n\n", $username);
+        $text = sprintf(lang('100108'), $username);
 
         if ($renewalSuccessArr) {
-            $text .= '续期成功:';
+            $text .= lang('100109');
             $text .= $this->genDomainsMarkDownText($renewalSuccessArr);
         }
 
         if ($renewalFailuresArr) {
-            $text .= '续期出错:';
+            $text .= lang('100110');
             $text .= $this->genDomainsMarkDownText($renewalFailuresArr);
         }
 
-        $text .= "\n今次无需续期的域名及其剩余天数如下所示:\n\n";
+        $text .= lang('100111');
         $text .= $this->genDomainStatusMarkDownText($domainStatus);
 
         $text .= $this->getMarkDownFooter();
@@ -238,7 +238,7 @@ class TelegramBot extends MessageGateway
         } else if ($type === 3) {
             $content = $this->genDomainStatusFullMarkDownText($data['username'], $data['domainStatusArr']);
         } else {
-            throw new \Exception(lang('error_msg.100003'));
+            throw new \Exception(lang('100003'));
         }
 
         $isMarkdown = true;
@@ -288,7 +288,7 @@ class TelegramBot extends MessageGateway
 
             return $resp['ok'] ?? false;
         } catch (\Exception $e) {
-            system_log('Telegram 消息发送失败:<red>' . $e->getMessage() . '</red>');
+            system_log(sprintf(lang('100112'), $e->getMessage()));
 
             return false;
         }

+ 16 - 16
libs/MessageServices/WeChat.php

@@ -120,13 +120,13 @@ class WeChat extends MessageGateway
         if (isset($resp['errcode']) && $resp['errcode'] === 0 && isset($resp['access_token']) && isset($resp['expires_in'])) {
             $accessTokenFileText = sprintf("WECHAT_ACCESS_TOKEN=%s\nWECHAT_ACCESS_TOKEN_EXPIRES_AT=%s\n", $resp['access_token'], time() + $resp['expires_in']);
             if (file_put_contents($this->accessTokenFile, $accessTokenFileText) === false) {
-                throw new \Exception('企业微信 access_token 写入失败:' . $this->accessTokenFile);
+                throw new \Exception(lang('100113') . $this->accessTokenFile);
             }
 
             return $resp['access_token'];
         }
 
-        throw new \Exception('获取企业微信 access_token 失败:' . ($resp['errmsg'] ?? '未知原因'));
+        throw new \Exception(lang('100114') . ($resp['errmsg'] ?? lang('100115')));
     }
 
     /**
@@ -158,8 +158,8 @@ class WeChat extends MessageGateway
     {
         $footer = '';
 
-        $footer .= "\n更多信息可以参考 <a href=\"https://my.freenom.com/domains.php?a=renewals\">Freenom官网</a> 哦~";
-        $footer .= "\n\n(如果你不想每次执行都收到推送,请将 .env 中 NOTICE_FREQ 的值设为 0,使程序只在有续期操作时才推送)";
+        $footer .= lang('100116');
+        $footer .= lang('100117');
 
         return $footer;
     }
@@ -174,16 +174,16 @@ class WeChat extends MessageGateway
     public function genDomainStatusText(array $domainStatus)
     {
         if (empty($domainStatus)) {
-            return "无数据。\n";
+            return lang('100118');
         }
 
         $domainStatusText = '';
 
         foreach ($domainStatus as $domain => $daysLeft) {
-            $domainStatusText .= sprintf('<a href="http://%s">%s</a> 还有 <a href="http://%s">%d</a> 天到期,', $domain, $domain, $domain, $daysLeft);
+            $domainStatusText .= sprintf(lang('100119'), $domain, $domain, $domain, $daysLeft);
         }
 
-        $domainStatusText = rtrim($domainStatusText, ',') . "。\n";
+        $domainStatusText = rtrim(rtrim($domainStatusText, ' '), ',,') . lang('100120');
 
         return $domainStatusText;
     }
@@ -200,19 +200,19 @@ class WeChat extends MessageGateway
      */
     public function genDomainRenewalResultsText(string $username, array $renewalSuccessArr, array $renewalFailuresArr, array $domainStatus)
     {
-        $text = sprintf("账户 <a href=\"https://www.google.com\">%s</a> 这次续期的结果如下\n\n", $username);
+        $text = sprintf(lang('100121'), $username);
 
         if ($renewalSuccessArr) {
-            $text .= '续期成功:';
+            $text .= lang('100122');
             $text .= $this->genDomainsText($renewalSuccessArr);
         }
 
         if ($renewalFailuresArr) {
-            $text .= '续期出错:';
+            $text .= lang('100123');
             $text .= $this->genDomainsText($renewalFailuresArr);
         }
 
-        $text .= "\n今次无需续期的域名及其剩余天数如下所示:\n\n";
+        $text .= lang('100124');
         $text .= $this->genDomainStatusText($domainStatus);
 
         $text .= $this->getFooter();
@@ -230,7 +230,7 @@ class WeChat extends MessageGateway
      */
     public function genDomainStatusFullText(string $username, array $domainStatus)
     {
-        $markDownText = sprintf("我刚刚帮小主看了一下,账户 <a href=\"https://www.google.com\">%s</a> 今天并没有需要续期的域名。所有域名情况如下:\n\n", $username);
+        $markDownText = sprintf(lang('100125'), $username);
 
         $markDownText .= $this->genDomainStatusText($domainStatus);
 
@@ -270,7 +270,7 @@ class WeChat extends MessageGateway
         } else if ($type === 3) {
             $content = $this->genDomainStatusFullText($data['username'], $data['domainStatusArr']);
         } else {
-            throw new \Exception(lang('error_msg.100003'));
+            throw new \Exception(lang('100003'));
         }
 
         if ($subject !== '') {
@@ -293,7 +293,7 @@ class WeChat extends MessageGateway
 
             return $this->doSend($accessToken, $body);
         } catch (\Exception $e) {
-            system_log('企业微信送信失败:<red>' . $e->getMessage() . '</red>');
+            system_log(sprintf(lang('100126'), $e->getMessage()));
 
             return false;
         }
@@ -325,7 +325,7 @@ class WeChat extends MessageGateway
         $resp = (array)json_decode($resp, true);
 
         if (!isset($resp['errcode']) || !isset($resp['errmsg'])) {
-            throw new \Exception('企业微信接口未返回预期的数据响应,本次响应数据为:' . json_encode($resp, JSON_UNESCAPED_UNICODE));
+            throw new \Exception(lang('100127') . json_encode($resp, JSON_UNESCAPED_UNICODE));
         }
 
         if ($resp['errcode'] === 0) {
@@ -334,7 +334,7 @@ class WeChat extends MessageGateway
             $accessToken = $this->getAccessToken(true);
 
             if ($numOfRetries > 2) {
-                throw new \Exception('检测到多次提示 access_token 失效,可能是未能正确获取 access_token,请介入调查:' . $resp['errmsg']);
+                throw new \Exception(lang('100128') . $resp['errmsg']);
             }
 
             $numOfRetries++;

+ 155 - 0
resources/lang/en.php

@@ -0,0 +1,155 @@
+<?php
+/**
+ * 英文语言包
+ *
+ * @author mybsdc <[email protected]>
+ * @date 2018/8/10
+ * @time 14:39
+ */
+
+return [
+    'exception_msg' => [
+        '34520001' => 'It is detected that you have not configured your freenom account information, please change the account related items in the .env file, otherwise the program will not work properly',
+        '34520002' => 'Error logging into freenom. Error message: %s',
+        '34520003' => 'Domain data match failed, maybe you don\'t have a domain name or the page of freenom has changed causing the regular expression to fail, please contact the author promptly: https://github.com/luolongfei/freenom/issues',
+        '34520004' => 'token match failed, may be the regular expression failed due to the page revision, please contact the author promptly: https://github.com/luolongfei/freenom/issues',
+        '34520005' => 'The putenv() function is disabled and cannot write to environment variables, so the program does not work properly, please enable the putenv() function',
+        '34520006' => sprintf('Versions below php7 are not supported, the current version is %s, please upgrade to php7 or above', PHP_VERSION),
+        '34520007' => sprintf('The .env configuration file is automatically generated in the %s directory, please change the contents of the configuration file to your own', ROOT_PATH),
+        '34520008' => sprintf('Please copy the .env.example file in the %s directory to an .env file and change the contents of the .env file to your own', ROOT_PATH),
+        '34520009' => 'Error getting domain status, probably not logged in or login is invalid, please try again',
+        '34520010' => 'Missing curl module, can\'t send request, please check your php environment and add curl module at compile time',
+        '34520012' => 'You have not configured an email address for incoming mail and may not receive mail. Please change the value corresponding to TO in the .env file to the email address you use most often to receive domain-related emails from the robot email address',
+        '34520013' => 'Error getting domain status, error message: %s',
+    ],
+    'messages' => [
+        '100001' => 'The cookie value named WHMCSZH5eHTGhfvzP could not be obtained, so this login is not valid, please check if your account or password is correct',
+        '100002' => 'Not allowed to send empty content emails',
+        '100003' => 'Illegal message types',
+        '100004' => "Execution error: \n",
+        '100005' => 'Master, I just captured an anomaly',
+        '100006' => 'Execution error: <red>%s</red>',
+        '100007' => 'Cloud function executed successfully.',
+        '100008' => 'Cloud function execution failed.',
+        '100009' => 'Your runtime environment is a cloud function, all environment variables will be read directly from the environment, and those variables not found in the environment will be read directly from the .env.example file',
+        '100010' => 'If it is in the Tencent cloud function, you can refer here to modify or add environment variables without rebuilding: https://github.com/luolongfei/freenom/blob/main/resources/screenshot/scf03.png',
+        '100011' => 'If it is in Aliyun cloud function, you can directly edit the environment variable at [Function Details] -> [Function Configuration] -> [Environment Information]',
+        '100012' => 'Because you did not turn on the upgrade alert feature, you will not be notified when a new version is available. Change the value of NEW_VERSION_DETECTION in the .env file to 1 to turn the feature back on.',
+        '100013' => 'Your .env file is too old, the program will automatically update the relevant configuration items according to the .env.example file, don\'t panic, this operation will not have any effect on the existing data',
+        '100014' => '<green>Backup of .env file completed</green>, old file location is %s/.env.old',
+        '100015' => 'A new .env file has been generated',
+        '100016' => '<green>Data migration complete</green>, %d environment variable data migrated',
+        '100017' => '<green>Congratulations, .env file upgrade completed</green>',
+        '100018' => 'Error upgrading .env file: ',
+        '100019' => 'Error generating an .env file from an .env.example file',
+        '100020' => 'Error when backing up .env file to .env.old file',
+        '100021' => 'File does not exist: ',
+        '100022' => 'Failed to read file content: ',
+        '100023' => 'Github returns data inconsistent with expectations: ',
+        '100024' => 'Detect upgrade error: ',
+        '100025' => "Hello, we have released a new version of FreeNom renewal tool v%s at %s, and the version you are currently using is v%s, you can decide whether to upgrade to the new version according to your actual needs. The new version has the following updates or improvements: \n\n",
+        '100026' => 'For more information, please visit: ',
+        '100027' => '(This message will only be pushed once for the same new version. If you do not want to be notified of new releases, set the value of NEW_VERSION_DETECTION in the .env file to 0)',
+        '100028' => 'just now',
+        '100029' => 'minutes ago',
+        '100030' => 'hours ago',
+        '100031' => 'yesterday ',
+        '100032' => 'Error in conversion to human friendly time: ',
+        '100033' => 'There is a new version of the FreeNom renewal tool available, your current version is v%s and the latest version is v%s. For more information about the new version, please visit: %s',
+        '100034' => '<green>New versions of the FreeNom renewal tool are available, the latest version is v%s (%s)</green>',
+        '100035' => 'Hi, there is a new version (v%s) of the FreeNom renewal tool available and a message has been sent to you about the new version',
+        '100036' => 'Information about the new version has been sent to you, please check it.',
+        '100037' => 'Upgrade error: ',
+        '100038' => 'Version %s',
+        '100039' => 'Master, I just renewed your domain name!',
+        '100040' => 'Congratulations, successfully renewed <green>%d</green> domains, failed <green>%d</green> domains. %s',
+        '100041' => 'A detailed renewal result message has been sent to you, please check it.',
+        '100042' => "Account: %s\n Renewal results are as follows:\n",
+        '100043' => 'Report, no domains need to be renewed today',
+        '100044' => 'The current notification frequency is "only when there is a renewal operation", so no notification will be pushed this time',
+        '100045' => '%s: <green>executed successfully, there are no domains that need to be renewed this time. </green>',
+        '100046' => 'Renewal Request Error: %s, Domain ID: %s (Account: %s)',
+        '100047' => 'Specifically, an exception was thrown at line %d of the %s file. The exception is %s, so go check it out. (Account: %s)',
+        '100048' => 'Master, something went wrong: ',
+        '100049' => 'A total of <green>%d</green> freenom accounts were found',
+        '100050' => 'Start processing the first <green>%d</green> freenom account [%d/%d]',
+        '100051' => 'Error: <red>%s</red>',
+        '100052' => 'Error: <red>%s</red>',
+        '100053' => 'An instance of the class named %s already exists',
+        '100054' => 'Class named %s does not exist',
+        '100055' => 'Since the "%s" feature is not enabled, the message will not be sent via "%s" this time, even though the relevant configuration is detected.',
+        '100056' => 'The message service class %s must inherit and implement the MessageServiceInterface interface',
+        '100057' => 'Unexpected program termination',
+        '100058' => 'There may be an error, the error message collected is: ',
+        '100059' => 'Master, the program was unexpectedly terminated',
+        '100060' => 'Uncaught exceptions: ',
+        '100061' => "The specific exception is: \n",
+        '100062' => 'Master, Uncaught Exception',
+        '100063' => 'Start executing Aliyun cloud functions',
+        '100064' => 'Email',
+        '100065' => 'Telegram Bot',
+        '100066' => 'Corporate WeChat',
+        '100067' => 'Server Chan',
+        '100068' => 'Bark',
+        '100069' => 'At present, Gmail, QQ mailbox, 163 mailbox and Outlook mailbox are supported for auto-identification configuration. For other types of mailboxes or self-built mailboxes, please append all relevant items of "custom mailbox configuration" in the .env file, otherwise you can\'t use mail service.',
+        '100070' => 'No data.',
+        '100071' => '<a href="http://%s" rel="noopener" target="_blank">%s</a> expires in <span style="font-weight: bold; font-size: 16px;">%d</span> days, ',
+        '100072' => '.',
+        '100073' => 'Master',
+        '100074' => 'Project Author',
+        '100075' => 'Renewal success: %s<br>',
+        '100076' => 'Renewal error: %s<br>',
+        '100077' => 'Mail sending failure: ',
+        '100078' => "\nFor more information, please refer to: https://my.freenom.com/domains.php?a=renewals (click \"copy content\" to copy this URL)",
+        '100079' => "\n\n(if you don't want to receive a push every time you execute, set the value of NOTICE_FREQ in .env to 0 so that the program only pushes when there is a renewal operation)",
+        '100080' => "No data.\n",
+        '100081' => '%s expires in %d days, ',
+        '100082' => ".\n",
+        '100083' => "Account %s The result of this renewal is as follows\n\n",
+        '100084' => 'Renewal success: ',
+        '100085' => 'Renewal error: ',
+        '100086' => "\nThe domains that do not need to be renewed this time and their remaining days are shown below:\n\n",
+        '100087' => "I just checked for you and the account %s has no domains that need to be renewed today. The status of all domains is as follows:\n\n",
+        '100088' => 'Unknown reason',
+        '100089' => 'Bark Failed to send message: <red>%s</red>',
+        '100090' => "I just checked for you and the account [%s](#) does not have any domains that need to be renewed today. The status of all domains is as follows:\n\n",
+        '100091' => "\nFor more information, please refer to [Freenom official website](https://my.freenom.com/domains.php?a=renewals)",
+        '100092' => "\n\n(if you don't want to receive a push every time you execute, set the value of NOTICE_FREQ in .env to 0 so that the program only pushes when there is a renewal operation)",
+        '100093' => "No data.\n",
+        '100094' => '[%s](http://%s) expires in *%d* days, ',
+        '100095' => ".\n",
+        '100096' => "Account [%s](#) The results of this renewal are as follows\n\n",
+        '100097' => 'Renewal success: ',
+        '100098' => 'Renewal error: ',
+        '100099' => "\nThe domains that do not need to be renewed this time and their remaining days are shown below:\n\n",
+        '100100' => 'No known reason',
+        '100101' => 'ServerChan Failed to send message: <red>%s</red>',
+        '100102' => "I just checked for you and the account %s has no domains that need to be renewed today. The status of all domains is as follows:\n\n",
+        '100103' => "\nFor more information, please refer to [Freenom official website](https://my.freenom.com/domains.php?a=renewals)",
+        '100104' => "\n\n(if you don't want to receive a push every time you execute, set the value of NOTICE_FREQ in .env to 0 so that the program only pushes when there is a renewal operation)",
+        '100105' => "No data.\n",
+        '100106' => '[%s](http://%s) expires in *%d* days, ',
+        '100107' => ".\n",
+        '100108' => "Account %s The results of this renewal are as follows\n\n",
+        '100109' => 'Renewal success: ',
+        '100110' => 'Renewal error: ',
+        '100111' => "\nThe domains that do not need to be renewed this time and their remaining days are shown below:\n\n",
+        '100112' => 'Telegram failed to send a message: <red>%s</red>',
+        '100113' => 'Corporate WeChat access_token write failure: ',
+        '100114' => 'Get Corporate WeChat access_token Failed: ',
+        '100115' => 'No known reason',
+        '100116' => "\nFor more information, please refer to <a href=\"https://my.freenom.com/domains.php?a=renewals\">Freenom official website</a>",
+        '100117' => "\n\n(if you don't want to receive a push every time you execute, set the value of NOTICE_FREQ in .env to 0 so that the program only pushes when there is a renewal operation)",
+        '100118' => "No data.\n",
+        '100119' => '<a href="http://%s">%s</a> expires in <a href="http://%s">%d</a> days, ',
+        '100120' => ".\n",
+        '100121' => "Account <a href=\"https://www.google.com\">%s</a> The results of this renewal are as follows\n\n",
+        '100122' => 'Renewal success: ',
+        '100123' => 'Renewal error: ',
+        '100124' => "\nThe domains that do not need to be renewed this time and their remaining days are shown below:\n\n",
+        '100125' => "I just checked for you and the account <a href=\"https://www.google.com\">%s</a> has no domains that need to be renewed today. The status of all domains is as follows:\n\n",
+        '100126' => 'Corporate WeChat failed to send a message: <red>%s</red>',
+        '100127' => 'Corporate WeChat interface did not return the expected data response, this response data is: ',
+        '100128' => 'Multiple invalid access_token prompts have been detected, possibly because the access_token was not properly obtained, please intervene to investigate: ',
+    ],
+];

+ 129 - 4
resources/lang/zh.php

@@ -1,6 +1,6 @@
 <?php
 /**
- * 语言包
+ * 中文语言包
  *
  * @author mybsdc <[email protected]>
  * @date 2018/8/10
@@ -11,8 +11,8 @@ return [
     'exception_msg' => [
         '34520001' => '检测到你尚未配置 freenom 账户信息,请修改 .env 文件中与账户相关的项,否则程序无法正常运作',
         '34520002' => '登录 freenom 出错。错误信息:%s',
-        '34520003' => '域名数据匹配失败,可能是你暂时没有域名或者页面改版导致正则失效,请及时联系作者',
-        '34520004' => 'token 匹配失败,可能是页面改版导致正则失效,请及时联系作者',
+        '34520003' => '域名数据匹配失败,可能是你暂时没有域名或者页面改版导致正则失效,请及时联系作者:https://github.com/luolongfei/freenom/issues',
+        '34520004' => 'token 匹配失败,可能是页面改版导致正则失效,请及时联系作者:https://github.com/luolongfei/freenom/issues',
         '34520005' => 'putenv() 函数被禁用,无法写入环境变量导致程序无法正常运作,请启用 putenv() 函数',
         '34520006' => sprintf('不支持 php7 以下的版本,当前版本为%s,请升级到 php7 以上', PHP_VERSION),
         '34520007' => sprintf('已自动在%s目录下生成 .env 配置文件,请将配置文件中的各项内容修改为你自己的', ROOT_PATH),
@@ -22,9 +22,134 @@ return [
         '34520012' => '你尚未配置收信邮箱,可能无法收到通知邮件。请将 .env 文件中的 TO 对应的值改为你最常用的邮箱地址,用于接收机器人邮箱发出的域名相关邮件',
         '34520013' => '获取域名状态页面出错,错误信息:%s',
     ],
-    'error_msg' => [
+    'messages' => [
         '100001' => '未能取得名为 WHMCSZH5eHTGhfvzP 的 cookie 值,故本次登录无效,请检查你的账户或密码是否正确。',
         '100002' => '不允许发送空内容邮件',
         '100003' => '非法消息类型',
+        '100004' => "执行出错:\n",
+        '100005' => '主人,捕获异常',
+        '100006' => '执行出错:<red>%s</red>',
+        '100007' => '云函数执行成功。',
+        '100008' => '云函数执行失败。',
+        '100009' => '检测到运行环境为云函数,所有环境变量将直接从环境中读取,环境中找不到的变量,则直接从 .env.example 文件中读取',
+        '100010' => '如果是在腾讯云函数,可以参考此处修改或新增环境变量,无需重建:https://github.com/luolongfei/freenom/blob/main/resources/screenshot/scf03.png',
+        '100011' => '如果是在阿里云函数,可以直接在【函数详情】->【函数配置】->【环境信息】处编辑环境变量',
+        '100012' => '由于你没有开启升级提醒功能,故无法在有新版本可用时第一时间收到通知。将 .env 文件中 NEW_VERSION_DETECTION 的值改为 1 即可重新开启相关功能。',
+        '100013' => '检测到你的 .env 文件内容过旧,程式将根据 .env.example 文件自动更新相关配置项,不要慌张,此操作对已有数据不会有任何影响',
+        '100014' => '<green>已完成 .env 文件备份</green>,旧文件位置为 %s/.env.old',
+        '100015' => '已生成新 .env 文件',
+        '100016' => '<green>数据迁移完成</green>,共迁移 %d 条环境变量数据',
+        '100017' => '<green>恭喜,已成功完成 .env 文件升级</green>',
+        '100018' => '升级 .env 文件出错:',
+        '100019' => '从 .env.example 文件生成 .env 文件时出错',
+        '100020' => '备份 .env 文件到 .env.old 文件时出错',
+        '100021' => '文件不存在:',
+        '100022' => '读取文件内容失败:',
+        '100023' => 'Github 返回的数据与预期不一致:',
+        '100024' => '检测升级出错:',
+        '100025' => "见信好,我们在 %s 发布了新版 FreeNom 续期工具 v%s,而你当前正在使用的版本为 v%s,你可以根据自己的实际需要决定是否升级到新版本。今次新版有以下更新或改进:\n\n",
+        '100026' => '欲知更多信息,请访问:',
+        '100027' => '(本消息针对同一个新版只会推送一次,如果你不想收到新版本通知,将 .env 文件中的 NEW_VERSION_DETECTION 的值设为 0 即可)',
+        '100028' => '刚刚',
+        '100029' => '分钟前',
+        '100030' => '小时前',
+        '100031' => '昨天 ',
+        '100032' => '转人类友好时间出错:',
+        '100033' => 'FreeNom 续期工具有新的版本可用,你当前版本为 v%s,最新版本为 v%s。关于新版的详细信息,请访问:%s',
+        '100034' => '<green>FreeNom 续期工具有新的版本可用,最新版本为 v%s(%s)</green>',
+        '100035' => '主人,FreeNom 续期工具有新的版本(v%s)可用,新版相关情况已给到你',
+        '100036' => '有关新版的信息已送信给到你,请注意查收。',
+        '100037' => '升级出错:',
+        '100038' => '当前程序版本 %s',
+        '100039' => '主人,我刚刚帮你续期域名啦~',
+        '100040' => '恭喜,成功续期 <green>%d</green> 个域名,失败 <green>%d</green> 个域名。%s',
+        '100041' => '详细的续期结果已送信成功,请注意查收。',
+        '100042' => "账户:%s\n续期结果如下:\n",
+        '100043' => '报告,今天没有域名需要续期',
+        '100044' => '当前通知频率为「仅当有续期操作时」,故本次不会推送通知',
+        '100045' => '%s:<green>执行成功,今次没有需要续期的域名。</green>',
+        '100046' => '续期请求出错:%s,域名 ID:%s(账户:%s)',
+        '100047' => '具体是在%s文件的第%d行,抛出了一个异常。异常的内容是%s,快去看看吧。(账户:%s)',
+        '100048' => '主人,出错了,',
+        '100049' => '共发现 <green>%d</green> 个 freenom 账户',
+        '100050' => '开始处理第 <green>%d</green> 个 freenom 账户 [%d/%d]',
+        '100051' => '出错:<red>%s</red>',
+        '100052' => '出错:<red>%s</red>',
+        '100053' => '类 %s 的实例已存在',
+        '100054' => '类 %s 不存在',
+        '100055' => '由于没有启用「%s」功能,故本次不通过「%s」送信,尽管检测到相关配置。',
+        '100056' => '消息服务类 %s 必须继承并实现 MessageServiceInterface 接口',
+        '100057' => '程序意外终止',
+        '100058' => '可能存在错误,这边收集到的错误信息为:',
+        '100059' => '主人,程序意外终止',
+        '100060' => '未捕获的异常:',
+        '100061' => "具体的异常内容是:\n",
+        '100062' => '主人,未捕获的异常',
+        '100063' => '开始执行阿里云函数',
+        '100064' => '邮件',
+        '100065' => 'Telegram Bot',
+        '100066' => '企业微信',
+        '100067' => 'Server 酱',
+        '100068' => 'Bark',
+        '100069' => '目前支持Gmail、QQ邮箱、163邮箱以及Outlook邮箱自动识别配置,其它类型的邮箱或者自建邮箱,请在 .env 文件中追加“自定义邮箱配置”的所有相关项,否则无法使用邮件服务。',
+        '100070' => '无数据。',
+        '100071' => '<a href="http://%s" rel="noopener" target="_blank">%s</a>还有 <span style="font-weight: bold; font-size: 16px;">%d</span> 天到期,',
+        '100072' => '。',
+        '100073' => '主人',
+        '100074' => '作者',
+        '100075' => '续期成功:%s<br>',
+        '100076' => '续期出错:%s<br>',
+        '100077' => '邮件发送失败:',
+        '100078' => "\n更多信息可以参考:https://my.freenom.com/domains.php?a=renewals(点击“复制内容”即可复制此网址)",
+        '100079' => "\n\n(如果你不想每次执行都收到推送,请将 .env 中 NOTICE_FREQ 的值设为 0,使程序只在有续期操作时才推送)",
+        '100080' => "无数据。\n",
+        '100081' => '%s 还有 %d 天到期,',
+        '100082' => "。\n",
+        '100083' => "账户 %s 这次续期的结果如下\n\n",
+        '100084' => '续期成功:',
+        '100085' => '续期出错:',
+        '100086' => "\n今次无需续期的域名及其剩余天数如下所示:\n\n",
+        '100087' => "我刚刚帮小主看了一下,账户 %s 今天并没有需要续期的域名。所有域名情况如下:\n\n",
+        '100088' => '未知原因',
+        '100089' => 'Bark 送信失败:<red>%s</red>',
+        '100090' => "我刚刚帮小主看了一下,账户 [%s](#) 今天并没有需要续期的域名。所有域名情况如下:\n\n",
+        '100091' => "\n更多信息可以参考 [Freenom官网](https://my.freenom.com/domains.php?a=renewals) 哦~",
+        '100092' => "\n\n(如果你不想每次执行都收到推送,请将 .env 中 NOTICE_FREQ 的值设为 0,使程序只在有续期操作时才推送)",
+        '100093' => "无数据。\n",
+        '100094' => '[%s](http://%s) 还有 *%d* 天到期,',
+        '100095' => "。\n",
+        '100096' => "账户 [%s](#) 这次续期的结果如下\n\n",
+        '100097' => '续期成功:',
+        '100098' => '续期出错:',
+        '100099' => "\n今次无需续期的域名及其剩余天数如下所示:\n\n",
+        '100100' => '未知原因',
+        '100101' => 'Server酱 消息发送失败:<red>%s</red>',
+        '100102' => "我刚刚帮小主看了一下,账户 %s 今天并没有需要续期的域名。所有域名情况如下:\n\n",
+        '100103' => "\n更多信息可以参考 [Freenom官网](https://my.freenom.com/domains.php?a=renewals) 哦~",
+        '100104' => "\n\n(如果你不想每次执行都收到推送,请将 .env 中 NOTICE_FREQ 的值设为 0,使程序只在有续期操作时才推送)",
+        '100105' => "无数据。\n",
+        '100106' => '[%s](http://%s) 还有 *%d* 天到期,',
+        '100107' => "。\n",
+        '100108' => "账户 %s 这次续期的结果如下\n\n",
+        '100109' => '续期成功:',
+        '100110' => '续期出错:',
+        '100111' => "\n今次无需续期的域名及其剩余天数如下所示:\n\n",
+        '100112' => 'Telegram 消息发送失败:<red>%s</red>',
+        '100113' => '企业微信 access_token 写入失败:',
+        '100114' => '获取企业微信 access_token 失败:',
+        '100115' => '未知原因',
+        '100116' => "\n更多信息可以参考 <a href=\"https://my.freenom.com/domains.php?a=renewals\">Freenom官网</a> 哦~",
+        '100117' => "\n\n(如果你不想每次执行都收到推送,请将 .env 中 NOTICE_FREQ 的值设为 0,使程序只在有续期操作时才推送)",
+        '100118' => "无数据。\n",
+        '100119' => '<a href="http://%s">%s</a> 还有 <a href="http://%s">%d</a> 天到期,',
+        '100120' => "。\n",
+        '100121' => "账户 <a href=\"https://www.google.com\">%s</a> 这次续期的结果如下\n\n",
+        '100122' => '续期成功:',
+        '100123' => '续期出错:',
+        '100124' => "\n今次无需续期的域名及其剩余天数如下所示:\n\n",
+        '100125' => "我刚刚帮小主看了一下,账户 <a href=\"https://www.google.com\">%s</a> 今天并没有需要续期的域名。所有域名情况如下:\n\n",
+        '100126' => '企业微信送信失败:<red>%s</red>',
+        '100127' => '企业微信接口未返回预期的数据响应,本次响应数据为:',
+        '100128' => '检测到多次提示 access_token 失效,可能是未能正确获取 access_token,请介入调查:',
     ],
 ];

+ 380 - 0
resources/mail/en/no_renewal_required.html

@@ -0,0 +1,380 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+    <meta charset="UTF-8">
+    <title>Email notification</title>
+    <style>
+        .mmsgLetter {
+            width: 580px;
+            margin: 0 auto;
+            padding: 10px;
+            color: #333;
+            background: #fff;
+            border: 0px solid #aaa;
+            border-radius: 5px;
+            -webkit-box-shadow: 3px 3px 10px #999 !important;
+            -moz-box-shadow: 3px 3px 10px #999 !important;
+            box-shadow: 3px 3px 10px #999 !important;
+            font-family: Verdana, sans-serif;
+        }
+
+        .mmsgLetter a:link,
+        .mmsgLetter a:visited {
+            color: #407700;
+        }
+
+        .mmsgLetterContent {
+            text-align: left;
+            padding: 30px;
+            font-size: 14px;
+            line-height: 1.5;
+            /*background: url('../images/ting.jpg') no-repeat top right;*/
+        }
+
+        .mmsgLetterContent h3 {
+            color: #000;
+            font-size: 20px;
+            font-weight: bold;
+            margin: 20px 0 20px;
+            border-top: 2px solid #eee;
+            padding: 20px 0 0 0;
+            font-family: "微软雅黑", "黑体", "Lucida Grande", Verdana, sans-serif;
+        }
+
+        .mmsgLetterContent p {
+            margin: 20px 0;
+            padding: 0;
+        }
+
+        .mmsgLetterContent .salutation {
+            font-weight: bold;
+        }
+
+        .mmsgLetterContent .mmsgMoreInfo {
+        }
+
+        .mmsgLetterContent a.mmsgButton {
+            display: block;
+            float: left;
+            height: 40px;
+            text-decoration: none;
+            text-align: center;
+            cursor: pointer;
+        }
+
+        .mmsgLetterContent a.mmsgButton span {
+            display: block;
+            float: left;
+            padding: 0 25px;
+            height: 40px;
+            line-height: 36px;
+            font-size: 14px;
+            font-weight: bold;
+            color: #fff;
+            text-shadow: 1px 0 0 #235e00;
+        }
+
+        .mmsgLetterContent a.mmsgButton:link,
+        .mmsgLetterContent a.mmsgButton:visited {
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -40px;
+        }
+
+        .mmsgLetterContent a.mmsgButton:link span,
+        .mmsgLetterContent a.mmsgButton:visited span {
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 0;
+        }
+
+        .mmsgLetterContent a.mmsgButton:hover,
+        .mmsgLetterContent a.mmsgButton:active {
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -120px;
+        }
+
+        .mmsgLetterContent a.mmsgButton:hover span,
+        .mmsgLetterContent a.mmsgButton:active span {
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 -80px;
+        }
+
+        .mmsgLetterInscribe {
+            padding: 40px 0 0;
+        }
+
+        .mmsgLetterInscribe .mmsgAvatar {
+            float: left;
+        }
+
+        .mmsgLetterInscribe .mmsgName {
+            margin: 0 0 10px;
+        }
+
+        .mmsgLetterInscribe .mmsgSender {
+            margin: 0 0 0 54px;
+        }
+
+        .mmsgLetterInscribe .mmsgInfo {
+            font-size: 12px;
+            margin: 0;
+            line-height: 1.2;
+        }
+
+        .mmsgLetterHeader {
+            height: 23px;
+            /*background: url('../images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
+            background: 7px 0 repeat-x #FFF;
+            background-image: -webkit-repeating-linear-gradient(135deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
+            background-image: repeating-linear-gradient(-45deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
+            background-size: 110px 10px;
+        }
+
+        .mmsgLetterFooter {
+            margin: 16px;
+            text-align: center;
+            font-size: 12px;
+            color: #888;
+            text-shadow: 1px 0px 0px #eee;
+        }
+
+        .mmsgLetterClr {
+            clear: both;
+            overflow: hidden;
+            height: 1px;
+        }
+
+        .mmsgLetterUser {
+            padding: 10px 0;
+        }
+
+        .mmsgLetterUserItem {
+            padding: 0 0 20px 0;
+        }
+
+        .mmsgLetterUserAvatar {
+            height: 40px;
+            border: 1px solid #ccc;
+            padding: 2px;
+            display: block;
+            float: left;
+        }
+
+        .mmsgLetterUserAvatar img {
+            width: 40px;
+            height: 40px;
+        }
+
+        .mmsgLetterInfo {
+            margin-left: 48px;
+        }
+
+        .mmsgLetterName {
+            display: block;
+            color: #5fa207;
+            font-weight: bold;
+            margin-left: 10px;
+        }
+
+        .mmsgLetterDesc {
+            font-size: 12px;
+            float: left;
+            height: 43px;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
+        }
+
+        .mmsgLetterDesc div {
+            white-space: nowrap;
+            float: left;
+            height: 43px;
+            padding: 0 20px;
+            line-height: 40px;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
+        }
+
+        .mmsgLetterUser {
+        }
+
+        .mmsgLetterAvatar {
+            float: left;
+        }
+
+        .mmsgLetterInfo {
+            margin: 0 0 0 60px;
+        }
+
+        .mmsgLetterNickName {
+            font-size: 14px;
+            font-weight: bold;
+        }
+
+        .mmsgLetterUin {
+            font-size: 12px;
+            color: #666;
+        }
+
+        .mmsgLetterUser {
+            padding: 10px 0;
+        }
+
+        .mmsgLetterUserItem {
+            padding: 0 0 20px 0;
+        }
+
+        .mmsgLetterUserAvatar {
+            height: 40px;
+            border: 1px solid #ccc;
+            padding: 2px;
+            display: block;
+            float: left;
+        }
+
+        .mmsgLetterUserAvatar img {
+            width: 40px;
+            height: 40px;
+        }
+
+        .mmsgLetterInfo {
+            margin-left: 48px;
+        }
+
+        .mmsgLetterName {
+            display: block;
+            color: #5fa207;
+            font-weight: bold;
+            margin-left: 10px;
+            padding-top: 10px;
+        }
+
+        .mmsgLetterDesc {
+            font-size: 12px;
+            float: left;
+            height: 43px;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
+        }
+
+        .mmsgLetterDesc div {
+            white-space: nowrap;
+            float: left;
+            height: 43px;
+            padding: 0 20px;
+            line-height: 40px;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
+        }
+
+        .qmbox style,
+        .qmbox script,
+        .qmbox head,
+        .qmbox link,
+        .qmbox meta {
+            display: none !important;
+        }
+
+        #mailContentContainer .txt {
+            height: auto;
+        }
+
+        a {
+            color: #407700 !important;
+        }
+
+        .renewed {
+            color: #4882CE !important;
+        }
+
+        .notRenewed {
+            color: #EB1B2E !important;
+        }
+
+        .domainDays a {
+            color: #407700 !important;
+        }
+
+        .renewed a,
+        .notRenewed a,
+        .domainDays a {
+            /*color: inherit !important;*/
+            display: inline-block;
+            background: #ffffff;
+            min-width: 22px;
+            text-align: center;
+            padding: 4px 6px;
+            border-radius: 22px;
+            font-size: 14px;
+            font-weight: bold;
+            transition: 0.2s all ease-in-out;
+            text-decoration: none !important;
+            /*margin-right: 12px;*/
+        }
+
+        .renewed a,
+        .notRenewed a {
+            margin-right: 12px;
+        }
+
+        .renewed a {
+            color: inherit !important;
+            border: 2px solid #4882CE;
+        }
+
+        .notRenewed a {
+            color: inherit !important;
+            border: 2px solid #EB1B2E;
+        }
+
+        .domainDays a {
+            border: 2px solid #407700;
+        }
+
+        .renewed a:hover {
+            background-color: #4882CE;
+            color: #ffffff !important;
+        }
+
+        .notRenewed a:hover {
+            background-color: #EB1B2E;
+            color: #ffffff !important;
+        }
+
+        .domainDays a:hover {
+            background-color: #407700;
+            color: #ffffff !important;
+        }
+    </style>
+</head>
+<body>
+<div id="mailContentContainer" class="qmbox qm_con_body_content qqmail_webmail_only" style="">
+    <div style="background-color:#d0d0d0;background-image:url('../images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
+        <div class="mmsgLetter">
+            <div class="mmsgLetterHeader" style="height:23px;"></div>
+            <div class="mmsgLetterContent"
+                 style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('../images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
+                <div>
+                    <p>
+                        <br>I just checked for you and the account <a href="#" rel="noopener">%s</a> has no domains that
+                        need to be renewed today. The status of all domains is as follows:
+                        <br>
+                        <br><span class="domainDays">%s</span>
+                        <br><br>
+                        For more information, please refer to the <a
+                            href="https://my.freenom.com/domains.php?a=renewals" target="_blank" rel="noopener">Freenom
+                        official website</a>
+                        <br>
+                        <br>(If you don't want to receive a push every time you execute, set the value of NOTICE_FREQ in
+                        .env to 0 so that the program only pushes when there is a renewal operation)
+                    </p>
+                </div>
+                <div class="mmsgLetterInscribe" style="padding:40px 0 0;">
+                    <img class="mmsgAvatar" src="https://q2.qlogo.cn/headimg_dl?dst_uin=593198779&spec=100"
+                         style="float:left;width:40px;height:40px;" alt="Author avatar">
+                    <div class="mmsgSender" style="margin:0 0 0 54px;">
+                        <p class="mmsgName" style="margin:0 0 10px;">Im Robot</p>
+                        <p class="mmsgInfo" style="font-size:12px;margin:0;line-height:1.2;">
+                            Freenom auto-renewal team <br>
+                            <a href="mailto:[email protected]" rel="noopener" target="_blank">[email protected]</a>
+                        </p>
+                    </div>
+                </div>
+            </div>
+            <div class="mmsgLetterFooter"
+                 style="margin:16px;text-align:center;font-size:12px;color:#888;text-shadow:1px 0px 0px #eee;"></div>
+        </div>
+    </div>
+</div>
+</body>
+</html>

+ 370 - 0
resources/mail/en/notice.html

@@ -0,0 +1,370 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+    <meta charset="UTF-8">
+    <title>Email notification</title>
+    <style>
+        .mmsgLetter {
+            width: 580px;
+            margin: 0 auto;
+            padding: 10px;
+            color: #333;
+            background: #fff;
+            border: 0px solid #aaa;
+            border-radius: 5px;
+            -webkit-box-shadow: 3px 3px 10px #999 !important;
+            -moz-box-shadow: 3px 3px 10px #999 !important;
+            box-shadow: 3px 3px 10px #999 !important;
+            font-family: Verdana, sans-serif;
+        }
+
+        .mmsgLetter a:link,
+        .mmsgLetter a:visited {
+            color: #407700;
+        }
+
+        .mmsgLetterContent {
+            text-align: left;
+            padding: 30px;
+            font-size: 14px;
+            line-height: 1.5;
+            /*background: url('../images/ting.jpg') no-repeat top right;*/
+        }
+
+        .mmsgLetterContent h3 {
+            color: #000;
+            font-size: 20px;
+            font-weight: bold;
+            margin: 20px 0 20px;
+            border-top: 2px solid #eee;
+            padding: 20px 0 0 0;
+            font-family: "微软雅黑", "黑体", "Lucida Grande", Verdana, sans-serif;
+        }
+
+        .mmsgLetterContent p {
+            margin: 20px 0;
+            padding: 0;
+        }
+
+        .mmsgLetterContent .salutation {
+            font-weight: bold;
+        }
+
+        .mmsgLetterContent .mmsgMoreInfo {
+        }
+
+        .mmsgLetterContent a.mmsgButton {
+            display: block;
+            float: left;
+            height: 40px;
+            text-decoration: none;
+            text-align: center;
+            cursor: pointer;
+        }
+
+        .mmsgLetterContent a.mmsgButton span {
+            display: block;
+            float: left;
+            padding: 0 25px;
+            height: 40px;
+            line-height: 36px;
+            font-size: 14px;
+            font-weight: bold;
+            color: #fff;
+            text-shadow: 1px 0 0 #235e00;
+        }
+
+        .mmsgLetterContent a.mmsgButton:link,
+        .mmsgLetterContent a.mmsgButton:visited {
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -40px;
+        }
+
+        .mmsgLetterContent a.mmsgButton:link span,
+        .mmsgLetterContent a.mmsgButton:visited span {
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 0;
+        }
+
+        .mmsgLetterContent a.mmsgButton:hover,
+        .mmsgLetterContent a.mmsgButton:active {
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -120px;
+        }
+
+        .mmsgLetterContent a.mmsgButton:hover span,
+        .mmsgLetterContent a.mmsgButton:active span {
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 -80px;
+        }
+
+        .mmsgLetterInscribe {
+            padding: 40px 0 0;
+        }
+
+        .mmsgLetterInscribe .mmsgAvatar {
+            float: left;
+        }
+
+        .mmsgLetterInscribe .mmsgName {
+            margin: 0 0 10px;
+        }
+
+        .mmsgLetterInscribe .mmsgSender {
+            margin: 0 0 0 54px;
+        }
+
+        .mmsgLetterInscribe .mmsgInfo {
+            font-size: 12px;
+            margin: 0;
+            line-height: 1.2;
+        }
+
+        .mmsgLetterHeader {
+            height: 23px;
+            /*background: url('../images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
+            background: 7px 0 repeat-x #FFF;
+            background-image: -webkit-repeating-linear-gradient(135deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
+            background-image: repeating-linear-gradient(-45deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
+            background-size: 110px 10px;
+        }
+
+        .mmsgLetterFooter {
+            margin: 16px;
+            text-align: center;
+            font-size: 12px;
+            color: #888;
+            text-shadow: 1px 0px 0px #eee;
+        }
+
+        .mmsgLetterClr {
+            clear: both;
+            overflow: hidden;
+            height: 1px;
+        }
+
+        .mmsgLetterUser {
+            padding: 10px 0;
+        }
+
+        .mmsgLetterUserItem {
+            padding: 0 0 20px 0;
+        }
+
+        .mmsgLetterUserAvatar {
+            height: 40px;
+            border: 1px solid #ccc;
+            padding: 2px;
+            display: block;
+            float: left;
+        }
+
+        .mmsgLetterUserAvatar img {
+            width: 40px;
+            height: 40px;
+        }
+
+        .mmsgLetterInfo {
+            margin-left: 48px;
+        }
+
+        .mmsgLetterName {
+            display: block;
+            color: #5fa207;
+            font-weight: bold;
+            margin-left: 10px;
+        }
+
+        .mmsgLetterDesc {
+            font-size: 12px;
+            float: left;
+            height: 43px;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
+        }
+
+        .mmsgLetterDesc div {
+            white-space: nowrap;
+            float: left;
+            height: 43px;
+            padding: 0 20px;
+            line-height: 40px;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
+        }
+
+        .mmsgLetterUser {
+        }
+
+        .mmsgLetterAvatar {
+            float: left;
+        }
+
+        .mmsgLetterInfo {
+            margin: 0 0 0 60px;
+        }
+
+        .mmsgLetterNickName {
+            font-size: 14px;
+            font-weight: bold;
+        }
+
+        .mmsgLetterUin {
+            font-size: 12px;
+            color: #666;
+        }
+
+        .mmsgLetterUser {
+            padding: 10px 0;
+        }
+
+        .mmsgLetterUserItem {
+            padding: 0 0 20px 0;
+        }
+
+        .mmsgLetterUserAvatar {
+            height: 40px;
+            border: 1px solid #ccc;
+            padding: 2px;
+            display: block;
+            float: left;
+        }
+
+        .mmsgLetterUserAvatar img {
+            width: 40px;
+            height: 40px;
+        }
+
+        .mmsgLetterInfo {
+            margin-left: 48px;
+        }
+
+        .mmsgLetterName {
+            display: block;
+            color: #5fa207;
+            font-weight: bold;
+            margin-left: 10px;
+            padding-top: 10px;
+        }
+
+        .mmsgLetterDesc {
+            font-size: 12px;
+            float: left;
+            height: 43px;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
+        }
+
+        .mmsgLetterDesc div {
+            white-space: nowrap;
+            float: left;
+            height: 43px;
+            padding: 0 20px;
+            line-height: 40px;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
+        }
+
+        .qmbox style,
+        .qmbox script,
+        .qmbox head,
+        .qmbox link,
+        .qmbox meta {
+            display: none !important;
+        }
+
+        #mailContentContainer .txt {
+            height: auto;
+        }
+
+        a {
+            color: #407700 !important;
+        }
+
+        .renewed {
+            color: #4882CE !important;
+        }
+
+        .notRenewed {
+            color: #EB1B2E !important;
+        }
+
+        .domainDays a {
+            color: #407700 !important;
+        }
+
+        .renewed a,
+        .notRenewed a,
+        .domainDays a {
+            /*color: inherit !important;*/
+            display: inline-block;
+            background: #ffffff;
+            min-width: 22px;
+            text-align: center;
+            padding: 4px 6px;
+            border-radius: 22px;
+            font-size: 14px;
+            font-weight: bold;
+            transition: 0.2s all ease-in-out;
+            text-decoration: none !important;
+            /*margin-right: 12px;*/
+        }
+
+        .renewed a,
+        .notRenewed a {
+            margin-right: 12px;
+        }
+
+        .renewed a {
+            color: inherit !important;
+            border: 2px solid #4882CE;
+        }
+
+        .notRenewed a {
+            color: inherit !important;
+            border: 2px solid #EB1B2E;
+        }
+
+        .domainDays a {
+            border: 2px solid #407700;
+        }
+
+        .renewed a:hover {
+            background-color: #4882CE;
+            color: #ffffff !important;
+        }
+
+        .notRenewed a:hover {
+            background-color: #EB1B2E;
+            color: #ffffff !important;
+        }
+
+        .domainDays a:hover {
+            background-color: #407700;
+            color: #ffffff !important;
+        }
+    </style>
+</head>
+<body>
+<div id="mailContentContainer" class="qmbox qm_con_body_content qqmail_webmail_only" style="">
+    <div style="background-color:#d0d0d0;background-image:url('../images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
+        <div class="mmsgLetter">
+            <div class="mmsgLetterHeader" style="height:23px;"></div>
+            <div class="mmsgLetterContent"
+                 style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('../images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
+                <div>
+                    <p>
+                        %s
+                    </p>
+                </div>
+                <div class="mmsgLetterInscribe" style="padding:40px 0 0;">
+                    <img class="mmsgAvatar" src="https://q2.qlogo.cn/headimg_dl?dst_uin=593198779&spec=100"
+                         style="float:left;width:40px;height:40px;" alt="Author avatar">
+                    <div class="mmsgSender" style="margin:0 0 0 54px;">
+                        <p class="mmsgName" style="margin:0 0 10px;">Im Robot</p>
+                        <p class="mmsgInfo" style="font-size:12px;margin:0;line-height:1.2;">
+                            Freenom auto-renewal team <br>
+                            <a href="mailto:[email protected]" rel="noopener" target="_blank">[email protected]</a>
+                        </p>
+                    </div>
+                </div>
+            </div>
+            <div class="mmsgLetterFooter"
+                 style="margin:16px;text-align:center;font-size:12px;color:#888;text-shadow:1px 0px 0px #eee;"></div>
+        </div>
+    </div>
+</div>
+</body>
+</html>

+ 380 - 0
resources/mail/en/successful_renewal.html

@@ -0,0 +1,380 @@
+<!DOCTYPE html>
+<html lang="zh">
+<head>
+    <meta charset="UTF-8">
+    <title>Email notification</title>
+    <style>
+        .mmsgLetter {
+            width: 580px;
+            margin: 0 auto;
+            padding: 10px;
+            color: #333;
+            background: #fff;
+            border: 0px solid #aaa;
+            border-radius: 5px;
+            -webkit-box-shadow: 3px 3px 10px #999 !important;
+            -moz-box-shadow: 3px 3px 10px #999 !important;
+            box-shadow: 3px 3px 10px #999 !important;
+            font-family: Verdana, sans-serif;
+        }
+
+        .mmsgLetter a:link,
+        .mmsgLetter a:visited {
+            color: #407700;
+        }
+
+        .mmsgLetterContent {
+            text-align: left;
+            padding: 30px;
+            font-size: 14px;
+            line-height: 1.5;
+            /*background: url('../images/ting.jpg') no-repeat top right;*/
+        }
+
+        .mmsgLetterContent h3 {
+            color: #000;
+            font-size: 20px;
+            font-weight: bold;
+            margin: 20px 0 20px;
+            border-top: 2px solid #eee;
+            padding: 20px 0 0 0;
+            font-family: "微软雅黑", "黑体", "Lucida Grande", Verdana, sans-serif;
+        }
+
+        .mmsgLetterContent p {
+            margin: 20px 0;
+            padding: 0;
+        }
+
+        .mmsgLetterContent .salutation {
+            font-weight: bold;
+        }
+
+        .mmsgLetterContent .mmsgMoreInfo {
+        }
+
+        .mmsgLetterContent a.mmsgButton {
+            display: block;
+            float: left;
+            height: 40px;
+            text-decoration: none;
+            text-align: center;
+            cursor: pointer;
+        }
+
+        .mmsgLetterContent a.mmsgButton span {
+            display: block;
+            float: left;
+            padding: 0 25px;
+            height: 40px;
+            line-height: 36px;
+            font-size: 14px;
+            font-weight: bold;
+            color: #fff;
+            text-shadow: 1px 0 0 #235e00;
+        }
+
+        .mmsgLetterContent a.mmsgButton:link,
+        .mmsgLetterContent a.mmsgButton:visited {
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -40px;
+        }
+
+        .mmsgLetterContent a.mmsgButton:link span,
+        .mmsgLetterContent a.mmsgButton:visited span {
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 0;
+        }
+
+        .mmsgLetterContent a.mmsgButton:hover,
+        .mmsgLetterContent a.mmsgButton:active {
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -120px;
+        }
+
+        .mmsgLetterContent a.mmsgButton:hover span,
+        .mmsgLetterContent a.mmsgButton:active span {
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 -80px;
+        }
+
+        .mmsgLetterInscribe {
+            padding: 40px 0 0;
+        }
+
+        .mmsgLetterInscribe .mmsgAvatar {
+            float: left;
+        }
+
+        .mmsgLetterInscribe .mmsgName {
+            margin: 0 0 10px;
+        }
+
+        .mmsgLetterInscribe .mmsgSender {
+            margin: 0 0 0 54px;
+        }
+
+        .mmsgLetterInscribe .mmsgInfo {
+            font-size: 12px;
+            margin: 0;
+            line-height: 1.2;
+        }
+
+        .mmsgLetterHeader {
+            height: 23px;
+            /*background: url('../images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
+            background: 7px 0 repeat-x #FFF;
+            background-image: -webkit-repeating-linear-gradient(135deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
+            background-image: repeating-linear-gradient(-45deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
+            background-size: 110px 10px;
+        }
+
+        .mmsgLetterFooter {
+            margin: 16px;
+            text-align: center;
+            font-size: 12px;
+            color: #888;
+            text-shadow: 1px 0px 0px #eee;
+        }
+
+        .mmsgLetterClr {
+            clear: both;
+            overflow: hidden;
+            height: 1px;
+        }
+
+        .mmsgLetterUser {
+            padding: 10px 0;
+        }
+
+        .mmsgLetterUserItem {
+            padding: 0 0 20px 0;
+        }
+
+        .mmsgLetterUserAvatar {
+            height: 40px;
+            border: 1px solid #ccc;
+            padding: 2px;
+            display: block;
+            float: left;
+        }
+
+        .mmsgLetterUserAvatar img {
+            width: 40px;
+            height: 40px;
+        }
+
+        .mmsgLetterInfo {
+            margin-left: 48px;
+        }
+
+        .mmsgLetterName {
+            display: block;
+            color: #5fa207;
+            font-weight: bold;
+            margin-left: 10px;
+        }
+
+        .mmsgLetterDesc {
+            font-size: 12px;
+            float: left;
+            height: 43px;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
+        }
+
+        .mmsgLetterDesc div {
+            white-space: nowrap;
+            float: left;
+            height: 43px;
+            padding: 0 20px;
+            line-height: 40px;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
+        }
+
+        .mmsgLetterUser {
+        }
+
+        .mmsgLetterAvatar {
+            float: left;
+        }
+
+        .mmsgLetterInfo {
+            margin: 0 0 0 60px;
+        }
+
+        .mmsgLetterNickName {
+            font-size: 14px;
+            font-weight: bold;
+        }
+
+        .mmsgLetterUin {
+            font-size: 12px;
+            color: #666;
+        }
+
+        .mmsgLetterUser {
+            padding: 10px 0;
+        }
+
+        .mmsgLetterUserItem {
+            padding: 0 0 20px 0;
+        }
+
+        .mmsgLetterUserAvatar {
+            height: 40px;
+            border: 1px solid #ccc;
+            padding: 2px;
+            display: block;
+            float: left;
+        }
+
+        .mmsgLetterUserAvatar img {
+            width: 40px;
+            height: 40px;
+        }
+
+        .mmsgLetterInfo {
+            margin-left: 48px;
+        }
+
+        .mmsgLetterName {
+            display: block;
+            color: #5fa207;
+            font-weight: bold;
+            margin-left: 10px;
+            padding-top: 10px;
+        }
+
+        .mmsgLetterDesc {
+            font-size: 12px;
+            float: left;
+            height: 43px;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
+        }
+
+        .mmsgLetterDesc div {
+            white-space: nowrap;
+            float: left;
+            height: 43px;
+            padding: 0 20px;
+            line-height: 40px;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
+        }
+
+        .qmbox style,
+        .qmbox script,
+        .qmbox head,
+        .qmbox link,
+        .qmbox meta {
+            display: none !important;
+        }
+
+        #mailContentContainer .txt {
+            height: auto;
+        }
+
+        a {
+            color: #407700 !important;
+        }
+
+        .renewed {
+            color: #4882CE !important;
+        }
+
+        .notRenewed {
+            color: #EB1B2E !important;
+        }
+
+        .domainDays a {
+            color: #407700 !important;
+        }
+
+        .renewed a,
+        .notRenewed a,
+        .domainDays a {
+            /*color: inherit !important;*/
+            display: inline-block;
+            background: #ffffff;
+            min-width: 22px;
+            text-align: center;
+            padding: 4px 6px;
+            border-radius: 22px;
+            font-size: 14px;
+            font-weight: bold;
+            transition: 0.2s all ease-in-out;
+            text-decoration: none !important;
+            /*margin-right: 12px;*/
+        }
+
+        .renewed a,
+        .notRenewed a {
+            margin-right: 12px;
+        }
+
+        .renewed a {
+            color: inherit !important;
+            border: 2px solid #4882CE;
+        }
+
+        .notRenewed a {
+            color: inherit !important;
+            border: 2px solid #EB1B2E;
+        }
+
+        .domainDays a {
+            border: 2px solid #407700;
+        }
+
+        .renewed a:hover {
+            background-color: #4882CE;
+            color: #ffffff !important;
+        }
+
+        .notRenewed a:hover {
+            background-color: #EB1B2E;
+            color: #ffffff !important;
+        }
+
+        .domainDays a:hover {
+            background-color: #407700;
+            color: #ffffff !important;
+        }
+    </style>
+</head>
+<body>
+<div id="mailContentContainer" class="qmbox qm_con_body_content qqmail_webmail_only" style="">
+    <div style="background-color:#d0d0d0;background-image:url('../images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
+        <div class="mmsgLetter">
+            <div class="mmsgLetterHeader" style="height:23px;"></div>
+            <div class="mmsgLetterContent"
+                 style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('../images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
+                <div>
+                    <p>Master, I just renewed your domain name!
+                        <br>
+                        <br>Account <a href="#" rel="noopener">%s</a> The results of this renewal are as follows
+                        <br><br>
+                        <span class="renewed">%s</span>
+                        <br>
+                        <span class="notRenewed">%s</span>
+                        <br>In addition, the status of other domains is as follows: <br><br><span
+                                class="domainDays">%s</span>
+                        <br><br>For more information, please refer to the <a
+                                href="https://my.freenom.com/domains.php?a=renewals" target="_blank" rel="noopener">Freenom
+                            official website</a>
+                    </p>
+                </div>
+                <div class="mmsgLetterInscribe" style="padding:40px 0 0;">
+                    <img class="mmsgAvatar" src="https://q2.qlogo.cn/headimg_dl?dst_uin=593198779&spec=100"
+                         style="float:left;width:40px;height:40px;" alt="Author avatar">
+                    <div class="mmsgSender" style="margin:0 0 0 54px;">
+                        <p class="mmsgName" style="margin:0 0 10px;">Im Robot</p>
+                        <p class="mmsgInfo" style="font-size:12px;margin:0;line-height:1.2;">
+                            Freenom auto-renewal team <br>
+                            <a href="mailto:[email protected]" rel="noopener" target="_blank">[email protected]</a>
+                        </p>
+                    </div>
+                </div>
+            </div>
+            <div class="mmsgLetterFooter"
+                 style="margin:16px;text-align:center;font-size:12px;color:#888;text-shadow:1px 0px 0px #eee;"></div>
+        </div>
+    </div>
+</div>
+</body>
+</html>

+ 12 - 12
resources/mail/no_renewal_required.html → resources/mail/zh/no_renewal_required.html

@@ -28,7 +28,7 @@
             padding: 30px;
             font-size: 14px;
             line-height: 1.5;
-            /*background: url('images/ting.jpg') no-repeat top right;*/
+            /*background: url('../images/ting.jpg') no-repeat top right;*/
         }
 
         .mmsgLetterContent h3 {
@@ -76,22 +76,22 @@
 
         .mmsgLetterContent a.mmsgButton:link,
         .mmsgLetterContent a.mmsgButton:visited {
-            background: #338702 url('images/mmsgletter_2_btn.png') no-repeat right -40px;
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -40px;
         }
 
         .mmsgLetterContent a.mmsgButton:link span,
         .mmsgLetterContent a.mmsgButton:visited span {
-            background: url('images/mmsgletter_2_btn.png') no-repeat 0 0;
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 0;
         }
 
         .mmsgLetterContent a.mmsgButton:hover,
         .mmsgLetterContent a.mmsgButton:active {
-            background: #338702 url('images/mmsgletter_2_btn.png') no-repeat right -120px;
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -120px;
         }
 
         .mmsgLetterContent a.mmsgButton:hover span,
         .mmsgLetterContent a.mmsgButton:active span {
-            background: url('images/mmsgletter_2_btn.png') no-repeat 0 -80px;
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 -80px;
         }
 
         .mmsgLetterInscribe {
@@ -118,7 +118,7 @@
 
         .mmsgLetterHeader {
             height: 23px;
-            /*background: url('images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
+            /*background: url('../images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
             background: 7px 0 repeat-x #FFF;
             background-image: -webkit-repeating-linear-gradient(135deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
             background-image: repeating-linear-gradient(-45deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
@@ -175,7 +175,7 @@
             font-size: 12px;
             float: left;
             height: 43px;
-            background: url('images/mmsgletter_chat_right.gif') no-repeat right top;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
         }
 
         .mmsgLetterDesc div {
@@ -184,7 +184,7 @@
             height: 43px;
             padding: 0 20px;
             line-height: 40px;
-            background: url('images/mmsgletter_chat_left.gif') no-repeat left top;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
         }
 
         .mmsgLetterUser {
@@ -245,7 +245,7 @@
             font-size: 12px;
             float: left;
             height: 43px;
-            background: url('images/mmsgletter_chat_right.gif') no-repeat right top;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
         }
 
         .mmsgLetterDesc div {
@@ -254,7 +254,7 @@
             height: 43px;
             padding: 0 20px;
             line-height: 40px;
-            background: url('images/mmsgletter_chat_left.gif') no-repeat left top;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
         }
 
         .qmbox style,
@@ -334,10 +334,10 @@
 </head>
 <body>
 <div id="mailContentContainer" class="qmbox qm_con_body_content qqmail_webmail_only" style="">
-    <div style="background-color:#d0d0d0;background-image:url('images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
+    <div style="background-color:#d0d0d0;background-image:url('../images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
         <div class="mmsgLetter">
             <div class="mmsgLetterHeader" style="height:23px;"></div>
-            <div class="mmsgLetterContent" style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
+            <div class="mmsgLetterContent" style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('../images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
                 <div>
                     <p>
                         <br>我刚刚帮小主看了一下,账户 <a href="#" rel="noopener">%s</a> 今天并没有需要续期的域名。所有域名情况如下:

+ 12 - 12
resources/mail/notice.html → resources/mail/zh/notice.html

@@ -28,7 +28,7 @@
             padding: 30px;
             font-size: 14px;
             line-height: 1.5;
-            /*background: url('images/ting.jpg') no-repeat top right;*/
+            /*background: url('../images/ting.jpg') no-repeat top right;*/
         }
 
         .mmsgLetterContent h3 {
@@ -76,22 +76,22 @@
 
         .mmsgLetterContent a.mmsgButton:link,
         .mmsgLetterContent a.mmsgButton:visited {
-            background: #338702 url('images/mmsgletter_2_btn.png') no-repeat right -40px;
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -40px;
         }
 
         .mmsgLetterContent a.mmsgButton:link span,
         .mmsgLetterContent a.mmsgButton:visited span {
-            background: url('images/mmsgletter_2_btn.png') no-repeat 0 0;
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 0;
         }
 
         .mmsgLetterContent a.mmsgButton:hover,
         .mmsgLetterContent a.mmsgButton:active {
-            background: #338702 url('images/mmsgletter_2_btn.png') no-repeat right -120px;
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -120px;
         }
 
         .mmsgLetterContent a.mmsgButton:hover span,
         .mmsgLetterContent a.mmsgButton:active span {
-            background: url('images/mmsgletter_2_btn.png') no-repeat 0 -80px;
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 -80px;
         }
 
         .mmsgLetterInscribe {
@@ -118,7 +118,7 @@
 
         .mmsgLetterHeader {
             height: 23px;
-            /*background: url('images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
+            /*background: url('../images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
             background: 7px 0 repeat-x #FFF;
             background-image: -webkit-repeating-linear-gradient(135deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
             background-image: repeating-linear-gradient(-45deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
@@ -175,7 +175,7 @@
             font-size: 12px;
             float: left;
             height: 43px;
-            background: url('images/mmsgletter_chat_right.gif') no-repeat right top;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
         }
 
         .mmsgLetterDesc div {
@@ -184,7 +184,7 @@
             height: 43px;
             padding: 0 20px;
             line-height: 40px;
-            background: url('images/mmsgletter_chat_left.gif') no-repeat left top;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
         }
 
         .mmsgLetterUser {
@@ -245,7 +245,7 @@
             font-size: 12px;
             float: left;
             height: 43px;
-            background: url('images/mmsgletter_chat_right.gif') no-repeat right top;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
         }
 
         .mmsgLetterDesc div {
@@ -254,7 +254,7 @@
             height: 43px;
             padding: 0 20px;
             line-height: 40px;
-            background: url('images/mmsgletter_chat_left.gif') no-repeat left top;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
         }
 
         .qmbox style,
@@ -334,10 +334,10 @@
 </head>
 <body>
 <div id="mailContentContainer" class="qmbox qm_con_body_content qqmail_webmail_only" style="">
-    <div style="background-color:#d0d0d0;background-image:url('images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
+    <div style="background-color:#d0d0d0;background-image:url('../images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
         <div class="mmsgLetter">
             <div class="mmsgLetterHeader" style="height:23px;"></div>
-            <div class="mmsgLetterContent" style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
+            <div class="mmsgLetterContent" style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('../images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
                 <div>
                     <p>
                         %s

+ 12 - 12
resources/mail/successful_renewal.html → resources/mail/zh/successful_renewal.html

@@ -28,7 +28,7 @@
             padding: 30px;
             font-size: 14px;
             line-height: 1.5;
-            /*background: url('images/ting.jpg') no-repeat top right;*/
+            /*background: url('../images/ting.jpg') no-repeat top right;*/
         }
 
         .mmsgLetterContent h3 {
@@ -76,22 +76,22 @@
 
         .mmsgLetterContent a.mmsgButton:link,
         .mmsgLetterContent a.mmsgButton:visited {
-            background: #338702 url('images/mmsgletter_2_btn.png') no-repeat right -40px;
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -40px;
         }
 
         .mmsgLetterContent a.mmsgButton:link span,
         .mmsgLetterContent a.mmsgButton:visited span {
-            background: url('images/mmsgletter_2_btn.png') no-repeat 0 0;
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 0;
         }
 
         .mmsgLetterContent a.mmsgButton:hover,
         .mmsgLetterContent a.mmsgButton:active {
-            background: #338702 url('images/mmsgletter_2_btn.png') no-repeat right -120px;
+            background: #338702 url('../images/mmsgletter_2_btn.png') no-repeat right -120px;
         }
 
         .mmsgLetterContent a.mmsgButton:hover span,
         .mmsgLetterContent a.mmsgButton:active span {
-            background: url('images/mmsgletter_2_btn.png') no-repeat 0 -80px;
+            background: url('../images/mmsgletter_2_btn.png') no-repeat 0 -80px;
         }
 
         .mmsgLetterInscribe {
@@ -118,7 +118,7 @@
 
         .mmsgLetterHeader {
             height: 23px;
-            /*background: url('images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
+            /*background: url('../images/mmsgletter_2_bg_topline.png') repeat-x 0 0;*/
             background: 7px 0 repeat-x #FFF;
             background-image: -webkit-repeating-linear-gradient(135deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
             background-image: repeating-linear-gradient(-45deg, #4882CE, #4882CE 20px, #FFF 20px, #FFF 35px, #EB1B2E 35px, #EB1B2E 55px, #FFF 55px, #FFF 70px);
@@ -175,7 +175,7 @@
             font-size: 12px;
             float: left;
             height: 43px;
-            background: url('images/mmsgletter_chat_right.gif') no-repeat right top;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
         }
 
         .mmsgLetterDesc div {
@@ -184,7 +184,7 @@
             height: 43px;
             padding: 0 20px;
             line-height: 40px;
-            background: url('images/mmsgletter_chat_left.gif') no-repeat left top;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
         }
 
         .mmsgLetterUser {
@@ -245,7 +245,7 @@
             font-size: 12px;
             float: left;
             height: 43px;
-            background: url('images/mmsgletter_chat_right.gif') no-repeat right top;
+            background: url('../images/mmsgletter_chat_right.gif') no-repeat right top;
         }
 
         .mmsgLetterDesc div {
@@ -254,7 +254,7 @@
             height: 43px;
             padding: 0 20px;
             line-height: 40px;
-            background: url('images/mmsgletter_chat_left.gif') no-repeat left top;
+            background: url('../images/mmsgletter_chat_left.gif') no-repeat left top;
         }
 
         .qmbox style,
@@ -334,10 +334,10 @@
 </head>
 <body>
 <div id="mailContentContainer" class="qmbox qm_con_body_content qqmail_webmail_only" style="">
-    <div style="background-color:#d0d0d0;background-image:url('images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
+    <div style="background-color:#d0d0d0;background-image:url('../images/mmsgletter_2_bg.png');text-align:center;padding:40px;">
         <div class="mmsgLetter">
             <div class="mmsgLetterHeader" style="height:23px;"></div>
-            <div class="mmsgLetterContent" style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
+            <div class="mmsgLetterContent" style="text-align:left;padding: 0 30px;font-size:14px;line-height:1.5;/*background:url('../images/ting.jpg') no-repeat top right;background-size: 224px 224px;*/">
                 <div>
                     <p>主人,我刚刚帮你续期域名啦~
                         <br>

+ 9 - 9
run

@@ -50,8 +50,8 @@ use Luolongfei\Libs\Message;
 function customize_error_handler()
 {
     if (!is_null($error = error_get_last())) {
-        Log::error('程序意外终止', $error);
-        Message::send('可能存在错误,这边收集到的错误信息为:' . json_encode($error, JSON_UNESCAPED_UNICODE), '主人,程序意外终止');
+        Log::error(lang('100057'), $error);
+        Message::send(lang('100058') . json_encode($error, JSON_UNESCAPED_UNICODE), lang('100059'));
     }
 }
 
@@ -62,8 +62,8 @@ function customize_error_handler()
  */
 function exception_handler($e)
 {
-    Log::error('未捕获的异常:' . $e->getMessage());
-    Message::send("具体的异常内容是:\n" . $e->getMessage(), '主人,未捕获的异常');
+    Log::error(lang('100060') . $e->getMessage());
+    Message::send(lang('100061') . $e->getMessage(), lang('100062'));
 }
 
 /**
@@ -90,7 +90,7 @@ function main_handler($event, $context)
 function handler($event, $context)
 {
     $logger = $GLOBALS['fcLogger'];
-    $logger->info('开始执行阿里云函数');
+    $logger->info(lang('100063'));
 
     return run();
 }
@@ -108,13 +108,13 @@ function run()
 
         $class::getInstance()->$fn();
 
-        return IS_SCF ? '云函数执行成功。' : true;
+        return IS_SCF ? lang('100007') : true;
     } catch (\Exception $e) {
-        system_log(sprintf('执行出错:<red>%s</red>', $e->getMessage()), $e->getTrace());
-        Message::send("执行出错:\n" . $e->getMessage(), '主人,捕获异常');
+        system_log(sprintf(lang('100006'), $e->getMessage()), $e->getTrace());
+        Message::send(lang('100004') . $e->getMessage(), lang('100005'));
     }
 
-    return IS_SCF ? '云函数执行失败。' : false;
+    return IS_SCF ? lang('100008') : false;
 }
 
 run();