diff --git a/.env.example b/.env.example
index e62f315..4f99eaf 100644
--- a/.env.example
+++ b/.env.example
@@ -1,3 +1,6 @@
+# Time zone
+TIME_ZONE=Asia/Hong_Kong
+
# Bot Settings
BOT_NAME=
BOT_TOKEN=
@@ -8,5 +11,17 @@ REDIS_PORT=6379
REDIS_PASSWD=
REDIS_PREFIX=tgbot
+# IP Info
+ECHOIP_HOST=ip.343.re
+
+# KMS Server
+KMS_HOST=kms.343.re
+KMS_VLMCS=/usr/bin/vlmcs
+
# ChinaZ API
ICP_KEY=
+
+# CFOP Pic
+CFOP_GAN=BQACAgUAAxkBAAIBtGEOLnr4Q6D4Z_80bgfXq5xsZMeWAAKtAwACWy55VOU-SGKqc7aMIAQ
+CFOP_MFG=BQACAgUAAxkBAAIB3WEOVHKeYrrGhFo-GffB0W-tQRKlAALQAwACWy55VGny8ArGMkfoIAQ
+CFOP_YX=BQACAgUAAxkBAAIB32EOVISFQbgmir2abj6QkgqaSX1WAALRAwACWy55VMEuU9lCYTYWIAQ
diff --git a/functions/Curl.php b/functions/Curl.php
new file mode 100644
index 0000000..d7904b0
--- /dev/null
+++ b/functions/Curl.php
@@ -0,0 +1,37 @@
+run(array(
+ [CURLOPT_URL, $url],
+ [CURLOPT_RETURNTRANSFER, 1],
+ [CURLOPT_CONNECTTIMEOUT, $timeOut],
+ [CURLOPT_USERAGENT, $this->ua]
+ ));
+ }
+
+ public function post($url, $data, $timeOut = 30) { // curl模拟Post 默认30s超时
+ return $this->run(array(
+ [CURLOPT_URL, $url],
+ [CURLOPT_RETURNTRANSFER, 1],
+ [CURLOPT_CONNECTTIMEOUT, $timeOut],
+ [CURLOPT_USERAGENT, $this->ua],
+ [CURLOPT_POST, 1],
+ [CURLOPT_POSTFIELDS, $data]
+ ));
+ }
+
+ private function run($configs) { // 发起curl请求
+ $curl = curl_init();
+ foreach ($configs as $config) {
+ curl_setopt($curl, $config[0], $config[1]);
+ }
+ $content = curl_exec($curl);
+ curl_close($curl);
+ return $content;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/functions/DNS.php b/functions/DNS.php
new file mode 100644
index 0000000..98f8b72
--- /dev/null
+++ b/functions/DNS.php
@@ -0,0 +1,72 @@
+ip2long6($record['ipv6']);
+ }
+ sort($ipAddr); // 解析结果排序
+ foreach ($ipAddr as &$ip) {
+ $ip = $this->long2ip6($ip);
+ }
+ return $ipAddr;
+ }
+
+ public function resolveIP($domain) { // DNS解析IP记录 A/AAAA
+ $ipAddr = array();
+ $ipv4 = $this->resolveA($domain);
+ foreach ($ipv4 as $ip) {
+ $ipAddr[] = $ip;
+ }
+ $ipv6 = $this->resolveAAAA($domain);
+ foreach ($ipv6 as $ip) {
+ $ipAddr[] = $ip;
+ }
+ return $ipAddr;
+ }
+
+ public function ip2long6($ipv6) { // 压缩IPv6地址为long
+ $ip_n = inet_pton($ipv6);
+ $bits = 15;
+ while ($bits >= 0) {
+ $bin = sprintf("%08b", (ord($ip_n[$bits])));
+ $ipv6long = $bin.$ipv6long;
+ $bits--;
+ }
+ return gmp_strval(gmp_init($ipv6long, 2), 10);
+ }
+
+ public function long2ip6($ipv6long) { // 解压long为IPv6地址
+ $bin = gmp_strval(gmp_init($ipv6long, 10), 2);
+ if (strlen($bin) < 128) {
+ $pad = 128 - strlen($bin);
+ for ($i = 1; $i <= $pad; $i++) {
+ $bin = '0' . $bin;
+ }
+ }
+ $bits = 0;
+ while ($bits <= 7) {
+ $bin_part = substr($bin, ($bits * 16), 16);
+ $ipv6 .= dechex(bindec($bin_part)) . ':';
+ $bits++;
+ }
+ return inet_ntop(inet_pton(substr($ipv6, 0, -1)));
+ }
+}
+
+?>
diff --git a/functions/DNSSEC.php b/functions/DNSSEC.php
new file mode 100644
index 0000000..de49dc6
--- /dev/null
+++ b/functions/DNSSEC.php
@@ -0,0 +1,47 @@
+
diff --git a/functions/ExtractDomain.php b/functions/Domain.php
similarity index 50%
rename from functions/ExtractDomain.php
rename to functions/Domain.php
index cbcacae..8bc4adc 100644
--- a/functions/ExtractDomain.php
+++ b/functions/Domain.php
@@ -1,9 +1,25 @@
tldDB);
$res = $db->query('SELECT record FROM `list`;');
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
@@ -12,28 +28,21 @@ class extractDomain {
return $tlds; // Unicode字符使用Punycode编码
}
- private function isDomain($domain) { // 检测是否为域名
- preg_match('/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/', $domain, $match);
- return (count($match) != 0);
- }
-
- private function getDomain($url) { // 从URL获取域名
+ private function extractDomain($url) { // 从URL获取域名
$url = preg_replace('/^[\w]+:\/\//', '', $url); // 去除协议字段
$url = explode('?', $url)[0]; // 去除请求参数内容
$domain = explode('/', $url)[0]; // 分离域名
return (new Punycode)->encode($domain);
}
- private function getTld($domain) { // 搜索域名TLD
- $tlds = $this->getAllTlds(); // 获取TLD列表
+ private function getTld($domain) { // 匹配域名TLD
+ $tlds = $this->getTldList(); // 获取TLD列表
foreach ($tlds as $tld) {
if (substr($domain, -strlen($tld)) === $tld) { // 匹配测试
$target[] = $tld;
}
}
- if (count($target) === 0) {
- return ''; // 匹配不到TLD
- };
+ if (!isset($target)) { return null; } // 匹配不到TLD
$type = 0;
foreach ($target as $tld) { // 遍历可能的结果
$num = substr_count($tld, '.');
@@ -52,15 +61,13 @@ class extractDomain {
}
public function analyse($url) { // 分析域名信息
- $domain = $this->getDomain($url);
+ $domain = $this->extractDomain($url);
if (!$this->isDomain($domain)) { // 域名不合格
return array();
}
$tld = $this->getTld($domain);
- if ($tld == '') { // 匹配不到TLD
- return array(
- 'domain' => $domain
- );
+ if ($tld === null) { // 匹配不到TLD
+ return [ 'domain' => $domain ];
}
return array(
'domain' => $domain,
@@ -68,6 +75,26 @@ class extractDomain {
'site' => $this->getSite($domain, $tld)
);
}
+
+ public function getSubTld($tld) {
+ $db = new SqliteDB($this->tldDB);
+ $res = $db->query('SELECT record FROM `list` WHERE tld="' . $tld . '";');
+ while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
+ if ($row['record'] !== $tld) {
+ $subTld[] = $row['record'];
+ }
+ }
+ return $subTld;
+ }
+
+ public function icpTldInfo($tld) { // 查询TLD的ICP信息
+ $db = new SqliteDB($this->tldDB);
+ $info = $db->query('SELECT * FROM `icp` WHERE tld="' . $tld . '";');
+ $info = $info->fetchArray(SQLITE3_ASSOC);
+ if ($info == '') { return null; } // TLD不存在于ICP列表中
+ $info['site'] = json_decode(base64_decode($info['site']), true);
+ return $info;
+ }
}
?>
diff --git a/functions/RedisCache.php b/functions/RedisCache.php
index 8c1e29c..5c7a34a 100644
--- a/functions/RedisCache.php
+++ b/functions/RedisCache.php
@@ -1,6 +1,6 @@
connect($this->redisSetting['host'], $this->redisSetting['port']);
if ($this->redisSetting['passwd'] !== '') {
@@ -24,7 +24,7 @@ class redisCache {
return $redisValue;
}
- public function setData($key, $data, $cacheTTL = 600) { // 写入信息到Redis缓存 默认10min过期
+ public function setData($key, $data, $cacheTTL = 0) { // 写入Redis缓存 默认不过期
$redis = new Redis();
$redis->connect($this->redisSetting['host'], $this->redisSetting['port']);
if ($this->redisSetting['passwd'] !== '') {
@@ -32,7 +32,9 @@ class redisCache {
}
$redisKey = $this->redisSetting['prefix'] . $key;
$status = $redis->set($redisKey, $data); // 写入数据库
- $redis->pexpire($redisKey, $cacheTTL * 1000); // 设置过期时间 单位 ms = s * 1000
+ if ($cacheTTL > 0) {
+ $redis->pexpire($redisKey, $cacheTTL * 1000); // 设置过期时间 单位 ms = s * 1000
+ }
return $status;
}
}
diff --git a/functions/SqliteDB.php b/functions/SqliteDB.php
index 88aa2d5..731b41d 100644
--- a/functions/SqliteDB.php
+++ b/functions/SqliteDB.php
@@ -2,10 +2,10 @@
class SqliteDB extends SQLite3 { // Sqlite3数据库
public function __construct($filename) {
- $this->open($filename);
+ $this->open($filename); // 打开数据库连接
}
public function __destruct() {
- $this->close();
+ $this->close(); // 关闭数据库连接
}
}
diff --git a/main.php b/main.php
index 3e424e7..5cf0cab 100644
--- a/main.php
+++ b/main.php
@@ -2,17 +2,20 @@
require_once 'env.php';
require_once 'route.php';
+require_once 'functions/DNS.php';
+require_once 'functions/Curl.php';
+require_once 'functions/Domain.php';
+require_once 'functions/DNSSEC.php';
require_once 'functions/Punycode.php';
require_once 'functions/SqliteDB.php';
require_once 'functions/RedisCache.php';
require_once 'functions/TgInterface.php';
-require_once 'functions/ExtractDomain.php';
-
-fastcgi_finish_request(); // 断开请求连接
+fastcgi_finish_request(); // 断开连接
$env = loadEnv('.env'); // 载入环境变量
$apiToken = $env['BOT_TOKEN'];
$botAccount = $env['BOT_NAME']; // 机器人用户名
+ini_set('date.timezone', $env['TIME_ZONE']); // 设置时区
$apiPath = 'https://api.telegram.org/bot' . $apiToken; // Telegram API接口
$webhook = json_decode(file_get_contents("php://input"), TRUE); // Webhook接受信息
@@ -27,6 +30,7 @@ if ($isCallback) { // 回调请求模式
$messageFrom = $webhook['message']['from'];
}
$chat = $message['chat'];
+
$tgEnv = array(
'isGroup' => ($chat['type'] === 'group') ? true : false, // 是否为群组
'isCallback' => $isCallback, // 是否为回调请求
diff --git a/models/cfopPic.php b/models/cfopPic.php
index 0642895..4bd0686 100644
--- a/models/cfopPic.php
+++ b/models/cfopPic.php
@@ -1,91 +1,74 @@
'网页下载',
+ 'url' => 'https://res.dnomd343.top/Share/cfop/'
+ ]),
+ array([
+ 'text' => '获取全部',
+ 'callback_data' => '/cfop all'
+ ]),
+ array(
+ array(
+ 'text' => 'GAN',
+ 'callback_data' => '/cfop gan'
+ ),
+ array(
+ 'text' => '魔方格',
+ 'callback_data' => '/cfop mfg'
+ ),
+ array(
+ 'text' => '裕鑫',
+ 'callback_data' => '/cfop yx'
+ )
+ )
+ );
+ tgApi::sendMessage(array(
'text' => 'CFOP魔方公式合集',
'reply_markup' => json_encode(array(
- 'inline_keyboard' => array(
- array([
- 'text' => '网页下载',
- 'url' => 'https://res.dnomd343.top/Share/cfop/'
- ]),
- array([
- 'text' => '获取全部',
- 'callback_data' => '/cfop all'
- ]),
- array(
- array(
- 'text' => 'GAN',
- 'callback_data' => '/cfop gan'
- ),
- array(
- 'text' => '魔方格',
- 'callback_data' => '/cfop mfg'
- ),
- array(
- 'text' => '裕鑫',
- 'callback_data' => '/cfop yx'
- )
- )
- )
+ 'inline_keyboard' => $buttons
))
- );
+ ));
}
- private function getPicId($type) { // 返回图片文件ID
+ private function sendCfopPic($type) { // 发送图片
global $env;
- switch ($type) {
- case 'gan':
- return $env['CFOP_GAN'];
- case 'mfg':
- return $env['CFOP_MFG'];
- case 'yx':
- return $env['CFOP_YX'];
- }
- }
-
- private function getPic($type) { // 获取图片
- switch ($type) {
- case 'gan':
- case 'mfg':
- case 'yx':
- return array(
- 'document' => $this->getPicId($type)
- );
- case '':
- return $this->getCfopMsg();
- default:
- return array(
- 'text' => '未知公式'
- );
- }
- }
-
- private function sendCfopPic($params) { // 发送图片或信息
- if ($params['document']) {
- tgApi::sendDocument($params);
+ if ($type === 'gan') {
+ $picId = $env['CFOP_GAN'];
+ } else if ($type === 'mfg') {
+ $picId = $env['CFOP_MFG'];
+ } else if ($type === 'yx') {
+ $picId = $env['CFOP_YX'];
+ } else if ($type === '' || $type === 'help') {
+ $this->sendMenu();
} else {
- tgApi::sendMessage($params);
+ tgApi::sendText('未知公式');
+ }
+ if (isset($picId)) {
+ tgApi::sendDocument(array(
+ 'document' => $picId
+ ));
}
}
public function query($rawParam) { // CFOP图片查询入口
- $this->sendCfopPic($this->getPic($rawParam));
+ $this->sendCfopPic($rawParam);
}
public function callback($rawParam) { // CFOP图片回调入口
- if ($rawParam === 'all') {
- global $tgEnv;
- tgApi::deleteMessage(array( // 删除源消息
- 'message_id' => $tgEnv['messageId']
- ));
- $this->sendCfopPic($this->getPic('gan'));
- $this->sendCfopPic($this->getPic('mfg'));
- $this->sendCfopPic($this->getPic('yx'));
+ if ($rawParam !== 'all') {
+ $this->sendCfopPic($rawParam);
return;
}
- $this->sendCfopPic($this->getPic($rawParam));
+ $this->sendCfopPic('gan');
+ $this->sendCfopPic('mfg');
+ $this->sendCfopPic('yx');
+ tgApi::deleteMessage(array( // 删除源消息
+ 'message_id' => $GLOBALS['tgEnv']['messageId']
+ ));
}
}
diff --git a/models/icpQuery.php b/models/icpQuery.php
index 9e5814c..60f2fcd 100644
--- a/models/icpQuery.php
+++ b/models/icpQuery.php
@@ -1,161 +1,56 @@
apiPath = $env['ICP_API'] . '?key=' . $env['ICP_KEY'] . '&domain=';
+ $this->apiPath = 'https://apidatav2.chinaz.com/single/icp?key=' . $GLOBALS['env']['ICP_KEY'] . '&domain=';
}
private function getIcpInfo($domain) { // ICP备案查询
- $domain = urlencode((new Punycode)->decode($domain));
- $info = json_decode(file_get_contents($this->apiPath . $domain), true);
- if ($info['StateCode'] === 1) { // 存在ICP备案
- $info = array(
- 'status' => 'ok',
+ $domain = urlencode((new Punycode)->decode($domain)); // API接口需要URL编码原始域名
+ $info = json_decode(file_get_contents($this->apiPath . $domain), true); // 请求API接口
+ if ($info['StateCode'] === 1) {
+ return $info['Result'] + array( // 存在ICP备案
+ 'time' => time(),
'hasIcp' => true
- ) + $info['Result'];
- } else if ($info['StateCode'] === 0) { // 无ICP备案
- if ($info['Reason'] !== '暂无备案信息') {
- return array(
- 'status' => 'error',
- 'message' => $info['Reason']
- );
- }
- $info = array(
- 'status' => 'ok',
- 'hasIcp' => false,
- 'icpMsg' => $info['Reason']
);
- } else {
- $info = array(
- 'status' => 'error', // 服务错误
- 'message' => 'Server error'
+ } else if ($info['StateCode'] === 0) {
+ if ($info['Reason'] !== '暂无备案信息') { return null; } // 服务错误
+ return array( // 无ICP备案
+ 'time' => time(),
+ 'hasIcp' => false
);
+ } else {
+ return null; // 服务错误
}
- return $info;
}
public function isCache($domain) { // 查询域名是否存在缓存
- $redis = new redisCache('icp');
+ $redis = new RedisCache('icp');
$info = $redis->getData($domain); // 查询缓存数据
return ($info) ? true : false;
}
- public function icpInfo($domain) { // ICP查询入口 带缓存
- $redis = new redisCache('icp');
+ public function icpInfo($domain, $isCache = true) { // ICP查询入口 默认带缓存
+ $redis = new RedisCache('icp');
$info = $redis->getData($domain); // 查询缓存数据
- if (!$info) { // 缓存未命中
- $info = $this->getIcpInfo($domain); // 执行查询
- if ($info['status'] !== 'ok') { // 查询错误
- return $info;
- }
- unset($info['status']);
- $redis->setData($domain, json_encode($info), 90 * 86400); // 缓存90day
+ if (!$isCache || !$info) { // 不缓存 或 缓存未命中
+ $info = $this->getIcpInfo($domain); // 发起查询
+ if ($info === null) { return null; } // 服务错误
+ $redis->setData($domain, json_encode($info)); // 缓存ICP信息 永久
} else { // 缓存命中
$info = json_decode($info, true); // 使用缓存数据
}
- return array(
- 'status' => 'ok'
- ) + $info;
+ return $info;
}
}
-class icpQueryEntry {
- private function getIcpTlds() { // 获取所有可ICP备案的顶级域
- $db = new SqliteDB('./db/tldInfo.db');
- $punycode = new Punycode();
- $res = $db->query('SELECT tld FROM `icp`;');
- while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
- $tlds[] = $punycode->encode($row['tld']); // 转为Punycode编码
- }
- return $tlds; // Unicode字符使用Punycode编码
- }
-
- private function isIcpEnable($tld) { // 检查TLD是否允许ICP备案
- $icpTlds = $this->getIcpTlds();
- foreach ($icpTlds as $icpTld) { // 遍历所有可ICP域名
- if ($icpTld === $tld) {
- return true; // 允许备案
- }
- }
- return false; // 无法备案
- }
-
- private function check($str) { // 检查输入参数
- $content = (new extractDomain)->analyse($str);
- if (!isset($content['domain'])) { // 格式错误
- return array(
- 'status' => 'error',
- 'message' => 'Illegal Request'
- );
- }
- if (!isset($content['tld'])) { // 未知TLD
- return array(
- 'status' => 'error',
- 'message' => 'Unknow TLD'
- );
- }
- if (!$this->isIcpEnable($content['tld'])) {
- return array(
- 'status' => 'error',
- 'message' => '`' . $content['tld'] . '` is not allowed in ICP'
- );
- }
- return array(
- 'status' => 'ok',
- 'domain' => $content['site'] // 返回主域名
- );
- }
-
- public function query($rawParam) { // ICP备案查询入口
- if ($rawParam == '' || $rawParam === 'help') { // 显示使用说明
- tgApi::sendMessage(array(
- 'parse_mode' => 'Markdown',
- 'text' => '*Usage:* `/icp domain`',
- ));
- return;
- }
- $content = $this->check($rawParam);
- if ($content['status'] !== 'ok') { // 请求参数错误
- tgApi::sendMarkdown($content['message']);
- return;
- }
- $isCache = true;
- $domain = $content['domain'];
+class icpQueryEntry { // ICP信息查询入口
+ private function genMessage($domain, $info) { // 生成ICP数据消息
+ if ($info === null) { return 'Server error'; } // 服务错误
$msg = '`' . (new Punycode)->decode($domain) . '`' . PHP_EOL;
- if (!(new icpQuery)->isCache($domain)) { // 域名信息未缓存
- $message = tgApi::sendMarkdown($msg . 'ICP备案信息查询中...');
- $message = json_decode($message, true);
- $isCache = false;
- }
- $info = (new icpQuery)->icpInfo($domain); // 发起查询
- if ($info['status'] !== 'ok') {
- if ($isCache) { // 没有缓冲信息 直接发送
- tgApi::sendText($info['message']); // 查询出错
- } else {
- tgApi::editMessage(array(
- 'text' => $info['message'],
- 'message_id' => $message['result']['message_id']
- ));
- }
- return;
- }
- if (!$info['hasIcp']) { // 没有ICP备案
- $content = array(
- 'parse_mode' => 'Markdown',
- 'text' => $msg . $info['icpMsg']
- );
- if ($isCache) { // 没有缓冲信息 直接发送
- tgApi::sendMessage($content);
- } else {
- tgApi::editMessage(array(
- 'message_id' => $message['result']['message_id']
- ) + $content);
- }
- return;
- }
+ if (!$info['hasIcp']) { return $msg . '暂无备案信息'; } // 无ICP备案
$msg .= '*类型:*' . $info['CompanyType'] . PHP_EOL;
if ($info['Owner'] != '') { // 负责人为空不显示
$msg .= '*负责人:*' . $info['Owner'] . PHP_EOL;
@@ -172,16 +67,53 @@ class icpQueryEntry {
$msg .= '*网站名:*' . $info['SiteName'] . PHP_EOL;
$msg .= '*审核时间:*' . $info['VerifyTime'] . PHP_EOL;
$msg .= '*许可证号:*' . $info['SiteLicense'] . PHP_EOL;
- if ($isCache) { // 没有缓冲信息 直接发送
- tgApi::sendMarkdown($msg); // 返回查询数据
- } else {
- tgApi::editMessage(array( // 返回查询数据 修改原消息
+ return $msg;
+ }
+
+ private function sendIcpInfo($domain) { // 查询并发送ICP备案信息
+ if (!(new icpQuery)->isCache($domain)) { // 未缓存
+ $headerMsg = '`' . (new Punycode)->decode($domain) . '`' . PHP_EOL;
+ $message = tgApi::sendMarkdown($headerMsg . 'ICP备案信息查询中...'); // 发送缓冲信息
+ $messageId = json_decode($message, true)['result']['message_id'];
+ $info = (new icpQuery)->icpInfo($domain); // 发起查询
+ tgApi::editMessage(array(
'parse_mode' => 'Markdown',
- 'text' => $msg,
- 'message_id' => $message['result']['message_id']
+ 'message_id' => $messageId, // 替换原消息
+ 'text' => $this->genMessage($domain, $info)
));
+ } else { // 已缓存
+ $info = (new icpQuery)->icpInfo($domain); // 获取缓存信息
+ $message = tgApi::sendMarkdown($this->genMessage($domain, $info)); // 先输出
+ if (time() - $info['time'] < 90 * 86400) { return; } // 缓存有效期90day
+ $infoRenew = (new icpQuery)->icpInfo($domain, false); // 不带缓存查询
+ $messageId = json_decode($message, true)['result']['message_id'];
+ unset($info['time']);
+ unset($infoRenew['time']);
+ if ($info !== $infoRenew) { // 数据出现变化
+ tgApi::editMessage(array(
+ 'parse_mode' => 'Markdown',
+ 'message_id' => $messageId,
+ 'text' => $this->genMessage($domain, $infoRenew) // 更新信息
+ ));
+ }
+ }
+ }
+
+ public function query($rawParam) { // ICP备案查询入口
+ if ($rawParam == '' || $rawParam === 'help') {
+ tgApi::sendMarkdown('*Usage:* `/icp domain`'); // 显示使用说明
+ return;
+ }
+ $content = (new Domain)->analyse($rawParam);
+ if (!isset($content['domain'])) { // 格式错误
+ tgApi::sendText('Illegal Request');
+ } else if (!isset($content['tld'])) { // 未知TLD
+ tgApi::sendText('Unknow TLD');
+ } else if ((new Domain)->icpTldInfo($content['tld']) === null) { // TLD无法ICP备案
+ tgApi::sendMarkdown('`' . $content['tld'] . '` is not allowed in ICP');
+ } else {
+ $this->sendIcpInfo($content['site']); // 查询并输出域名备案信息
}
-
}
}
diff --git a/models/ipInfo.php b/models/ipInfo.php
index 8d73e01..e41d724 100644
--- a/models/ipInfo.php
+++ b/models/ipInfo.php
@@ -1,11 +1,10 @@
apiPath = $env['ECHOIP_API'];
+ $this->apiPath = 'https://' . $GLOBALS['env']['ECHOIP_HOST'] . '/info/';
}
private function changeCoor($num) { // 转为时分秒格式
@@ -27,83 +26,32 @@ class ipInfo {
return $str;
}
- public function getInfo($ip) {
- if (!filter_var($ip, FILTER_VALIDATE_IP)) { // IP地址不合法
- return array(
- 'status' => 'error',
- 'message' => 'Illegal IP Address'
- );
- }
- $redis = new redisCache('ip');
+ private function getIpInfo($ip) { // 向上游查询IP信息
+ $content = (new Curl)->get($this->apiPath . $ip);
+ $info = json_decode($content, true);
+ if ($info['status'] !== 'T') { return null; }
+ unset($info['status']);
+ return $info + array(
+ 'locCoor' => $this->coorFormat($info['loc']) // 经纬度格式
+ );
+ }
+
+ public function getInfo($ip) { // 查询IP信息 错误返回null
+ $redis = new RedisCache('ip');
$info = $redis->getData($ip); // 查询缓存数据
if (!$info) { // 缓存未命中
- $info = json_decode(file_get_contents($this->apiPath . $ip), true);
- if ($info['status'] !== 'T') { // 上游接口错误
- return array(
- 'status' => 'error',
- 'message' => 'Server Error'
- );
- }
- unset($info['status']);
- if ($info['loc'] != NULL) {
- $info['locCoor'] = $this->coorFormat($info['loc']); // 转换为经纬度格式
- }
- $redis->setData($ip, json_encode($info), 1800); // 缓存30min
+ $info = $this->getIpInfo($ip);
+ if ($info === null) { return null; } // 服务错误 返回null
+ $redis->setData($ip, json_encode($info), 43200); // 缓存12h
} else { // 缓存命中
$info = json_decode($info, true); // 使用缓存数据
}
- return array(
- 'status' => 'ok',
- 'data' => json_encode($info) // 返回查询结果
- );
+ return $info; // 查询成功 返回结果
}
}
-class ipInfoEntry {
- private function isDomain($domain) { // 检测是否为域名
- preg_match('/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/', $domain, $match);
- return (count($match) != 0);
- }
-
- private function dnsResolveIPv4($domain) { // DNS解析A记录
- $ipAddr = array();
- $rs = dns_get_record(strtolower($domain), DNS_A);
- foreach ($rs as $record) {
- $ipAddr[] = $record['ip'];
- }
- return $ipAddr;
- }
-
- private function dnsResolveIPv6($domain) { // DNS解析AAAA记录
- $ipAddr = array();
- $rs = dns_get_record(strtolower($domain), DNS_AAAA);
- foreach ($rs as $record) {
- $ipAddr[] = $record['ipv6'];
- }
- return $ipAddr;
- }
-
- private function dnsResolve($domain) { // DNS解析IP记录
- $ipAddr = array();
- $ipv4 = $this->dnsResolveIPv4($domain);
- foreach ($ipv4 as $ip) {
- $ipAddr[] = $ip;
- }
- $ipv6 = $this->dnsResolveIPv6($domain);
- foreach ($ipv6 as $ip) {
- $ipAddr[] = $ip;
- }
- return $ipAddr;
- }
-
- private function getInfo($ip) {
- $content = (new ipInfo)->getInfo($ip); // 发起查询
- if ($content['status'] !== 'ok') {
- return array(
- 'text' => $content['message'] // 返回错误信息
- );
- }
- $info = json_decode($content['data'], true);
+class ipInfoEntry { // IP信息查询入口
+ private function genMessage($info) { // 生成返回信息
$msg = 'IP: ' . $info['ip'] . '
' . PHP_EOL;
if ($info['as'] != NULL) {
$msg .= 'AS: ';
@@ -122,43 +70,37 @@ class ipInfoEntry {
if ($info['scope'] != NULL) { $msg .= 'Scope: ' . $info['scope'] . '
' . PHP_EOL; }
if ($info['detail'] != NULL) { $msg .= 'Detail: ' . $info['detail'] . PHP_EOL; }
return array(
+ 'text' => $msg,
'parse_mode' => 'HTML', // HTML格式输出
'disable_web_page_preview' => 'true', // 不显示页面预览
- 'text' => $msg,
- 'reply_markup' => json_encode(array( // 显示按钮
- 'inline_keyboard' => array([[
- 'text' => 'View on echoIP',
- 'url' => 'https://ip.dnomd343.top/?ip=' . $ip
- ]])
- ))
+ 'reply_markup' => $this->genButton('View on echoIP', $info['ip']) // 显示按钮
);
}
- public function query($rawParam) { // ipInfo查询入口
- if ($rawParam == '' || $rawParam === 'help') { // 显示使用说明
- tgApi::sendMessage(array(
- 'parse_mode' => 'Markdown',
- 'text' => '*Usage:* `/ip IPv4/IPv6/Domain`',
- 'reply_markup' => json_encode(array( // 显示echoIP按钮
- 'inline_keyboard' => array([[
- 'text' => 'Get my IP address',
- 'url' => 'https://ip.dnomd343.top/'
- ]])
- ))
- ));
- return;
- }
- if (filter_var($rawParam, FILTER_VALIDATE_IP)) { // 参数为IP地址
- tgApi::sendMessage($this->getInfo($rawParam)); // 发起查询
- return;
- }
- if (!$this->isDomain($rawParam)) {
- tgApi::sendText('Illegal Request'); // 非法请求
+ private function genButton($text, $ip = '') { // 生成ehcoIP页面链接按钮 默认为主页
+ $url = 'https://' . $GLOBALS['env']['ECHOIP_HOST'] . '/';
+ if ($ip !== '') { $url .= '?ip=' . $ip; }
+ return json_encode(array(
+ 'inline_keyboard' => array([[ // echoIP按钮
+ 'text' => $text,
+ 'url' => $url
+ ]])
+ ));
+ }
+
+ private function sendInfo($ip) { // 查询并发送IP信息
+ $info = (new ipInfo)->getInfo($ip);
+ if ($info === null) {
+ tgApi::sendText('Server error'); // 上游查询错误
return;
}
- $ips = $this->dnsResolve($rawParam);
+ tgApi::sendMessage($this->genMessage($info));
+ }
+
+ private function sendDomainInfo($domain) { // 查询并发送域名解析结果
+ $ips = (new DNS)->resolveIP($domain);
if (count($ips) == 0) { // 解析不到IP记录
- tgApi::sendMarkdown('Nothing found of `' . $rawParam . '`');
+ tgApi::sendMarkdown('Nothing found of `' . $domain . '`');
return;
}
foreach ($ips as $ip) {
@@ -170,27 +112,47 @@ class ipInfoEntry {
if (count($ips) >= 2) {
$buttons[] = array([ // 两个及以上的IP 添加显示全部的按钮
'text' => 'Get all detail',
- 'callback_data' => '/ip ' . $rawParam
+ 'callback_data' => '/ip ' . $domain
]);
}
tgApi::sendMessage(array(
'parse_mode' => 'Markdown',
- 'text' => 'DNS resolve of `' . $rawParam . '`',
- 'reply_markup' => json_encode(array( // 显示IP列表按钮
+ 'text' => 'DNS resolve of `' . $domain . '`',
+ 'reply_markup' => json_encode(array( // IP列表按钮
'inline_keyboard' => $buttons
))
));
}
+ private function sendHelp() { // 发送使用说明
+ $helpMessage = array(
+ 'parse_mode' => 'Markdown',
+ 'text' => '*Usage:* `/ip IPv4/IPv6/Domain`',
+ 'reply_markup' => $this->genButton('Get my IP address') // echoIP主页链接
+ );
+ tgApi::sendMessage($helpMessage);
+ }
+
+ public function query($rawParam) { // ipInfo查询入口
+ if ($rawParam == '' || $rawParam === 'help') {
+ $this->sendHelp(); // 显示使用说明
+ } else if (filter_var($rawParam, FILTER_VALIDATE_IP)) { // 参数为IP地址
+ $this->sendInfo($rawParam); // 查询并发送IP信息
+ } else if ((new Domain)->isDomain($rawParam)) { // 参数为域名
+ $this->sendDomainInfo($rawParam); // 查询并发送域名信息
+ } else {
+ tgApi::sendText('Illegal Request'); // 非法请求
+ }
+ }
+
public function callback($rawParam) { // ipInfo回调入口
if (filter_var($rawParam, FILTER_VALIDATE_IP)) { // 参数为IP地址
$this->query($rawParam);
} else { // 参数为域名
- global $tgEnv;
tgApi::deleteMessage(array(
- 'message_id' => $tgEnv['messageId']
+ 'message_id' => $GLOBALS['tgEnv']['messageId']
));
- $ips = $this->dnsResolve($rawParam);
+ $ips = (new DNS)->resolveIP($rawParam);
foreach ($ips as $ip) {
$this->query($ip); // 逐个输出
}
diff --git a/models/kmsCheck.php b/models/kmsCheck.php
index 560755b..bedfbe0 100644
--- a/models/kmsCheck.php
+++ b/models/kmsCheck.php
@@ -1,16 +1,16 @@
kmsDB);
- $res = $db->query('SELECT * FROM `' . $type . '_version` WHERE version_id=' . $version_id . ';');
- return $res->fetchArray(SQLITE3_ASSOC)['version_name'];
+class kmsKeys { // KMS密钥获取
+ private $db, $kmsDB = './db/kmsKeys.db'; // KMS密钥数据库
+
+ private function getVersionName($type, $versionId) { // 获取对应版本的名称
+ $res = $this->db->query('SELECT * FROM `' . $type . '_version` WHERE version_id=' . $versionId . ';');
+ return $res->fetchArray(SQLITE3_ASSOC)['version_name'];
}
- private function getKmsKeys($type) { // 获取所有版本的KMS密钥
- $db = new SqliteDB($this->kmsDB);
- $res = $db->query('SELECT * FROM `' . $type . '`;');
+ private function getKmsKeys($type) { // 获取指定类型的密钥信息 win/win-server
+ $this->db = new SqliteDB($this->kmsDB);
+ $res = $this->db->query('SELECT * FROM `' . $type . '`;');
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
$index = $row['version'];
unset($row['version']);
@@ -19,110 +19,101 @@ class kmsKeys {
return $data;
}
- public function getKeys($type) { // 获取指定类型KMS密钥
+ public function getKeys($type) { // 获取指定类型的各版本及KMS密钥
switch ($type) {
- case '':
+ case '': // win和win-server系列
return $this->getKmsKeys('win') + $this->getKmsKeys('win-server');
- case 'win':
+ case 'win': // win系列
return $this->getKmsKeys('win');
- case 'win-server':
+ case 'win-server': // win-server系列
return $this->getKmsKeys('win-server');
default:
- return array();
+ return array(); // 未知类型 返回空数组
}
}
}
-class kmsCheck {
- private $api;
+class kmsCheck { // KMS服务器检查
+ private function getKmsStatus($host, $port) { // 获取KMS服务器状态
+ if(filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ $host = '[' . $host . ']'; // IPv6地址需用中括号包围
+ }
+ $cmd = $GLOBALS['env']['KMS_VLMCS'] . ' ';
+ $cmd .= $host . ':' . $port; // 加入目标服务器信息
+ $raw = shell_exec($cmd . ' -G temp'); // 执行vlmcs测试
+ preg_match_all('/Sending activation request \(KMS V6\)/', $raw, $match);
+ return (count($match[0]) === 6) ? true : false; // 返回KMS服务器状态
+ }
- public function __construct() {
- global $env;
- $this->api = $env['KMS_API'];
+ public function isCache($server) { // 检查KMS服务器是否已缓存
+ $redis = new RedisCache('kms');
+ $status = $redis->getData($server);
+ return (!$status) ? false : true;
}
- public function kmsStatus($host, $port) {
+ public function kmsStatus($host, $port) { // 检测KMS服务器状态
+ $redis = new RedisCache('kms');
$server = $host . ':' . $port;
- $redis = new redisCache('kms');
- $info = $redis->getData($server); // 查询缓存数据
- if (!$info) { // 缓存未命中
- $url = $this->api . 'check?host=' . $host . '&port=' . $port;
- $info = json_decode(file_get_contents($url), true); // 请求上游接口
- $info['server'] = $server;
- switch ($info['status']) {
- case 'ok':
- $info['online'] = true;
- break;
- case 'error':
- $info['online'] = false;
- break;
- default:
- return array(
- 'status' => 'error',
- 'message' => 'Server error'
- );
+ $status = $redis->getData($server);
+ if (!$status) { // 缓存未命中
+ if ($this->getKmsStatus($host, $port)) { // 测试服务器状态
+ $status = [ 'status' => 'online' ]; // 服务正常
+ } else {
+ $status = [ 'status' => 'offline' ]; // 服务掉线
}
- $info['status'] = 'ok';
- unset($info['message']);
- $redis->setData($server, json_encode($info), 300); // 缓存5min
+ $redis->setData($server, json_encode($status), 300); // 缓存5min
} else { // 缓存命中
- $info = json_decode($info, true); // 使用缓存数据
+ $status = json_decode($status, true); // 使用缓存数据
}
- return $info;
+ return $status;
}
}
-class kmsCheckEntry {
- private function isHost($host) { // 判断host是否合法
- preg_match('/^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/', $host, $match);
- if (count($match) !== 0) { // 域名
- if (!is_numeric(substr($host, -1))) { return true; } // 域名最后一位不为数字
- }
- if (filter_var($host, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4)) { // IPv4地址
- return true;
- }
- return false;
- }
-
- private function formatCheck($server) {
+class kmsCheckEntry { // KMS功能入口
+ private function formatCheck($server) { // 输入参数格式检查
$temp = explode(':', $server);
if (count($temp) === 1) { // 不带:的请求
- if ($this->isHost($temp[0])) {
- $host = $server;
- $port = 1688;
- } else {
- return array(
- 'status' => 'error',
- 'message' => 'Illegal host'
- );
- }
+ if (!(new Domain)->isHost($temp[0])) { return null; } // 错误请求
+ return array(
+ 'host' => $temp[0],
+ 'port' => 1688
+ );
} else if (count($temp) === 2) { // 带一个:的请求
- if ($this->isHost($temp[0])) {
- $host = $temp[0];
- } else {
- return array(
- 'status' => 'error',
- 'message' => 'Illegal host'
- );
- }
- $port = $temp[1];
- if ($port < 0 || $port > 65535) {
- return array(
- 'status' => 'error',
- 'message' => 'Illegal port'
- );
- }
- } else { // 带多个:的请求
+ if (!(new Domain)->isHost($temp[0])) { return null; } // 错误请求
+ if ($temp[1] < 0 || $temp[1] > 65535) { return null; } // 错误请求
return array(
- 'status' => 'error',
- 'message' => 'Illegal request'
+ 'host' => $temp[0],
+ 'port' => $temp[1]
);
+ } else { // 带多个:的请求
+ return null; // 错误请求
}
- return array(
- 'status' => 'ok',
- 'host' => $host,
- 'port' => $port
- );
+ }
+
+ private function genMessage($server, $status) { // 生成KMS状态消息
+ $msg = '`' . $server . '`' . PHP_EOL;
+ if ($status['status'] === 'online') {
+ return $msg . 'KMS服务*正常运行*'; // 服务正常
+ } else {
+ return $msg . 'KMS服务*无法使用*'; // 服务异常
+ }
+ }
+
+ private function sendKmsStatus($host, $port) { // 检查并发送KMS服务器状态
+ $server = $host . ':' . $port;
+ if ((new kmsCheck)->isCache($server)) { // 状态已缓存
+ $status = (new kmsCheck)->kmsStatus($host, $port);
+ tgApi::sendMarkdown($this->genMessage($server, $status)); // 发送服务器状态
+ return;
+ }
+ $message = tgApi::sendMarkdown('`' . $server . '`' . PHP_EOL . 'KMS服务检测中...');
+ $messageId = json_decode($message, true)['result']['message_id']; // 未缓存 发送缓冲消息
+ $status = (new kmsCheck)->kmsStatus($host, $port); // 发起查询
+ tgApi::editMessage(array( // 返回查询结果
+ 'parse_mode' => 'Markdown',
+ 'message_id' => $messageId,
+ 'text' => $this->genMessage($server, $status)
+ ));
}
private function simpStr($str) { // 简化版本名称
@@ -130,17 +121,41 @@ class kmsCheckEntry {
return implode('', $match[0]);
}
- private function getKmsVersions($type) { // 获取win或win-server的版本列表
+ private function genKmsKeys($targetVersion) { // 显示指定版本的KMS密钥列表
+ $content = explode('|', $targetVersion);
+ $type = $content[0]; // 获取类型 win/win-server
+ $targetVersion = $content[1]; // 获取目标版本
+ $kmsKeys = (new kmsKeys)->getKeys($type); // 获取该类型的所有版本
+ foreach ($kmsKeys as $version => $kmsKey) { // 比对压缩以后的名称
+ if ($this->simpStr($version) === $targetVersion) { break; } // 匹配成功
+ }
+ $msg = '*' . $version . ' KMS Keys*' . PHP_EOL . PHP_EOL;
+ foreach ($kmsKey as $row) {
+ $msg .= $row['name'] . ':`' . $row['key'] . '`' . PHP_EOL . PHP_EOL;
+ }
+ return array(
+ 'text' => $msg,
+ 'parse_mode' => 'Markdown',
+ 'reply_markup' => json_encode(array( // 返回按钮
+ 'inline_keyboard' => array([[
+ 'text' => '<< Go back <<',
+ 'callback_data' => '/kms ' . $type
+ ]])
+ ))
+ );
+ }
+
+ private function genKmsVersions($type) { // 获取版本列表 win/win-server
$kmsKeys = (new kmsKeys)->getKeys($type);
foreach ($kmsKeys as $version => $kmsKey) {
$buttons[] = array([ // 生成按钮列表
'text' => $version,
- 'callback_data' => '/kms ' . $this->simpStr($version)
+ 'callback_data' => '/kms ' . $type . '|' . $this->simpStr($version)
]);
}
$buttons[] = array([
'text' => '<< Go back <<',
- 'callback_data' => '/kms keys'
+ 'callback_data' => '/kms menu' // 加入返回按钮
]);
return array(
'text' => 'Which version did you need?',
@@ -150,122 +165,91 @@ class kmsCheckEntry {
);
}
- private function getKmsKeys($targetVersion) { // 显示指定版本的KMS密钥列表
- $kmsKeys = (new kmsKeys)->getKeys('');
- foreach ($kmsKeys as $version => $kmsKey) { // 比对压缩以后的名称
- if ($this->simpStr($version) === $targetVersion) { break; } // 匹配成功
- }
- $msg = '*' . $version . ' KMS Keys*' . PHP_EOL . PHP_EOL;
- foreach ($kmsKey as $row) {
- $msg .= $row['name'] . ':`' . $row['key'] . '`' . PHP_EOL . PHP_EOL;
- }
+ private function genActiveCmd() { // 生成KMS激活命令
+ $kmsHost = $GLOBALS['env']['KMS_HOST'];
+ $actiCmd = '```' . PHP_EOL . 'slmgr /upk' . PHP_EOL . 'slmgr /ipk {KMS_KEY}' . PHP_EOL;
+ $actiCmd .= 'slmgr /skms ' . $kmsHost . PHP_EOL . 'slmgr /ato' . PHP_EOL . 'slmgr /dlv';
+ $actiCmd .= PHP_EOL . '```';
return array(
+ 'text' => $actiCmd,
'parse_mode' => 'Markdown',
- 'text' => $msg
+ 'reply_markup' => json_encode(array( // 返回按钮
+ 'inline_keyboard' => array([[
+ 'text' => '<< Go back <<',
+ 'callback_data' => '/kms menu'
+ ]])
+ ))
);
}
- private function checkKms($host, $port) { // 检查KMS服务器状态
- $content = (new kmsCheck)->kmsStatus($host, $port);
- if ($content['status'] === 'ok') {
- if ($content['online'] === true) {
- return array(
- 'parse_mode' => 'Markdown',
- 'text' => '`' . $content['server'] . '`' . PHP_EOL . 'KMS服务*正常运行*'
- );
- } else {
- return array(
- 'parse_mode' => 'Markdown',
- 'text' => '`' . $content['server'] . '`' . PHP_EOL . 'KMS服务*无法使用*'
- );
- }
- } else {
- return array(
- 'text' => $content['message']
- );
- }
- }
-
- public function query($rawParam) { // kmsCheck查询入口
- if ($rawParam == '' || $rawParam === 'help') { // 显示使用说明
- tgApi::sendMessage(array(
- 'parse_mode' => 'Markdown',
- 'text' => '*Usage:* `/kms IP/Domain[:port]`',
- 'reply_markup' => json_encode(array( // 获取KMS密钥按钮
- 'inline_keyboard' => array([[
- 'text' => 'Get KMS Keys',
- 'callback_data' => '/kms keys'
- ]])
- ))
- ));
- return;
- }
- $check = $this->formatCheck($rawParam);
- if ($check['status'] === 'error') { // 输入格式有误
- tgApi::sendText($check['message']);
- return;
- }
- $message = tgApi::sendMarkdown('`' . $rawParam . '`' . PHP_EOL . 'KMS服务检测中...');
- $message = json_decode($message, true);
- fastcgi_finish_request(); // 断开连接
- tgApi::editMessage(array(
- 'message_id' => $message['result']['message_id'],
- ) + $this->checkKms($check['host'], $check['port'])); // 发起查询并返回结果
- }
-
- public function callback($rawParam) { // kmsCheck回调入口
- global $tgEnv;
- $selectMsg = array(
+ private function genSelectMsg() { // 生成KMS版本选择消息
+ return array(
'text' => 'Which one did you need?',
'reply_markup' => json_encode(array(
- 'inline_keyboard' => array(
+ 'inline_keyboard' => array( // 功能选择按钮
array([
'text' => 'Windows',
- 'callback_data' => '/kms win'
+ 'callback_data' => '/kms win' // Windows密钥
]),
array([
'text' => 'Windows Server',
- 'callback_data' => '/kms win-server'
+ 'callback_data' => '/kms win-server' // Windows Server密钥
]),
array([
'text' => 'Activation Command',
- 'callback_data' => '/kms cmd'
+ 'callback_data' => '/kms cmd' // 激活命令
])
)
))
);
- $actiCmd = '```' . PHP_EOL . 'slmgr /upk' . PHP_EOL . 'slmgr /ipk {KMS_KEY}' . PHP_EOL;
- $actiCmd .= 'slmgr /skms {KMS_HOST}' . PHP_EOL . 'slmgr /ato' . PHP_EOL . 'slmgr /dlv';
- $actiCmd .= PHP_EOL . '```';
+ }
+
+ private function sendHelp() { // 发送使用说明
+ $helpMessage = array(
+ 'parse_mode' => 'Markdown',
+ 'text' => '*Usage:* `/kms IP/Domain[:port]`',
+ 'reply_markup' => json_encode(array( // 加入 获取KMS密钥 按钮
+ 'inline_keyboard' => array([[
+ 'text' => 'Get KMS Keys',
+ 'callback_data' => '/kms menu'
+ ]])
+ ))
+ );
+ tgApi::sendMessage($helpMessage);
+ }
+
+ public function query($rawParam) { // KMS测试查询入口
+ if ($rawParam == '' || $rawParam === 'help') { // 显示使用说明
+ $this->sendHelp();
+ return;
+ }
+ $server = $this->formatCheck($rawParam);
+ if ($server === null) {
+ tgApi::sendText('Illegal request'); // 输入格式错误
+ return;
+ }
+ $this->sendKmsStatus($server['host'], $server['port']); // 检查并发送KMS服务器状态
+ }
+
+ public function callback($rawParam) { // KMS测试回调入口
+ $messageId = $GLOBALS['tgEnv']['messageId'];
switch ($rawParam) {
- case 'keys':
- tgApi::editMessage(array(
- 'message_id' => $tgEnv['messageId'],
- ) + $selectMsg);
- return;
- case 'cmd':
- tgApi::editMessage(array(
- 'message_id' => $tgEnv['messageId'],
- 'parse_mode' => 'Markdown',
- 'text' => $actiCmd,
- 'reply_markup' => json_encode(array(
- 'inline_keyboard' => array([[
- 'text' => '<< Go back <<',
- 'callback_data' => '/kms keys'
- ]])
- ))
- ));
- return;
- case 'win':
- case 'win-server':
- tgApi::editMessage(array(
- 'message_id' => $tgEnv['messageId'],
- ) + $this->getKmsVersions($rawParam));
- return;
+ case 'menu': // 选择菜单
+ $message = $this->genSelectMsg();
+ break;
+ case 'cmd': // KMS激活命令
+ $message = $this->genActiveCmd();
+ break;
+ case 'win': // Windows激活密钥
+ case 'win-server': // Windows Server激活密钥
+ $message = $this->genKmsVersions($rawParam);
+ break;
+ default:
+ $message = $this->genKmsKeys($rawParam); // 显示密钥列表
}
tgApi::editMessage(array(
- 'message_id' => $tgEnv['messageId']
- ) + $this->getKmsKeys($rawParam));
+ 'message_id' => $messageId // 修改源消息内容
+ ) + $message);
}
}
diff --git a/models/ntpCheck.php b/models/ntpCheck.php
index 306f2c4..c48f994 100644
--- a/models/ntpCheck.php
+++ b/models/ntpCheck.php
@@ -1,28 +1,27 @@
ntpDB);
- $res = $db->query('SELECT * FROM `ntp_list` WHERE id=' . $list_id . ';');
+ private function getListName($listId) { // 获取对应组的名称
+ $res = $this->db->query('SELECT * FROM `ntp_list` WHERE id=' . $listId . ';');
return $res->fetchArray(SQLITE3_ASSOC)['name'];
}
- public function getNtpList() { // 获取所有NTP服务器地址
- $db = new SqliteDB($this->ntpDB);
- $res = $db->query('SELECT * FROM `ntp_host`;');
+ public function getList() { // 获取所有NTP服务器地址
+ $this->db = new SqliteDB($this->ntpDB);
+ $res = $this->db->query('SELECT * FROM `ntp_host`;');
while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
$index = $row['list_id'];
unset($row['list_id']);
- $data[$this->getListName($index)][] = $row;
+ $list[$this->getListName($index)][] = $row;
}
- return $data;
+ return $list;
}
}
-class ntpCheck {
- private function formatOffset($str) { // 格式化Offset
+class ntpCheck { // NTP服务器检查
+ private function formatOffset($str) { // 格式化偏移时间
$num = number_format($str, 6) * 1000; // s -> ms
$str = sprintf("%1\$.3f", $num); // 补零到小数点后3位
if ($num > 0) {
@@ -31,23 +30,61 @@ class ntpCheck {
return $str . 'ms';
}
- private function curlPost($url, $data) { // curl模拟post操作 40s超时
- $curl = curl_init();
- curl_setopt($curl, CURLOPT_URL, $url);
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 40);
- curl_setopt($curl, CURLOPT_POST, 1);
- curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
- $content = curl_exec($curl);
- curl_close($curl);
- return $content;
+ private function sortByIp($servers) { // 排序算法
+ $temp = array();
+ foreach ($servers as $val){
+ $temp[] = $val['Server'];
+ }
+ sort($temp);
+ $temp = array_flip($temp);
+ $sort = array();
+ foreach ($servers as $val) {
+ $temp_1 = $val['Server'];
+ $temp_2 = $temp[$temp_1];
+ $sort[$temp_2] = $val;
+ }
+ asort($sort);
+ return $sort;
+ }
+
+ private function sortServer($servers) { // 按顺序排列服务器
+ foreach ($servers as $server) {
+ if(filter_var($server['Server'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+ $ipv4[] = $server; // 提取IPv4服务器
+ }
+ if(filter_var($server['Server'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
+ $ipv6[] = $server; // 提取IPv6服务器
+ }
+ }
+ if (isset($ipv4)) { // 存在IPv4服务器
+ foreach ($ipv4 as $index => $ip) {
+ $ipv4[$index]['Server'] = ip2long($ip['Server']); // IPv4预处理
+ }
+ $ipv4 = $this->sortByIp($ipv4); // 排序IPv4服务器
+ foreach ($ipv4 as $index => $ip) {
+ $ip['Server'] = long2ip($ip['Server']); // IPv4恢复
+ $result[] = $ip;
+ }
+ }
+ if (isset($ipv6)) { // 存在IPv6服务器
+ foreach ($ipv6 as $index => $ip) {
+ $ipv6[$index]['Server'] = (new DNS)->ip2long6($ip['Server']); // IPv6预处理
+ }
+ $ipv6 = $this->sortByIp($ipv6); // 排序IPv6服务器
+ foreach ($ipv6 as $index => $ip) {
+ $ip['Server'] = (new DNS)->long2ip6($ip['Server']); // IPv6恢复
+ $result[] = $ip;
+ }
+ }
+ return (!isset($result)) ? array() : $result; // 无结果 返回空数组
}
private function getNtpStatus($host) { // 获取NTP服务器状态
- $html = $this->curlPost('https://servertest.online/ntp', array(
+ $html = (new Curl)->post('https://servertest.online/ntp', array(
'a' => $host,
'c' => 'Query+both'
));
+ if ($html == '') { return null; } // 服务错误
preg_match('/<\/form>[\s\S]+