Browse Source

Merge branch 'dev' into master

master
Dnomd343 3 years ago
parent
commit
1dd2056a3d
  1. 15
      .env.example
  2. 37
      functions/Curl.php
  3. 72
      functions/DNS.php
  4. 47
      functions/DNSSEC.php
  5. 63
      functions/Domain.php
  6. 10
      functions/RedisCache.php
  7. 4
      functions/SqliteDB.php
  8. 10
      main.php
  9. 123
      models/cfopPic.php
  10. 208
      models/icpQuery.php
  11. 176
      models/ipInfo.php
  12. 354
      models/kmsCheck.php
  13. 213
      models/ntpCheck.php
  14. 4
      models/punycode.php
  15. 191
      models/tgDC.php
  16. 113
      models/tldQuery.php
  17. 3
      route.php

15
.env.example

@ -1,3 +1,6 @@
# Time zone
TIME_ZONE=Asia/Hong_Kong
# Bot Settings # Bot Settings
BOT_NAME= BOT_NAME=
BOT_TOKEN= BOT_TOKEN=
@ -8,5 +11,17 @@ REDIS_PORT=6379
REDIS_PASSWD= REDIS_PASSWD=
REDIS_PREFIX=tgbot REDIS_PREFIX=tgbot
# IP Info
ECHOIP_HOST=ip.343.re
# KMS Server
KMS_HOST=kms.343.re
KMS_VLMCS=/usr/bin/vlmcs
# ChinaZ API # ChinaZ API
ICP_KEY= ICP_KEY=
# CFOP Pic
CFOP_GAN=BQACAgUAAxkBAAIBtGEOLnr4Q6D4Z_80bgfXq5xsZMeWAAKtAwACWy55VOU-SGKqc7aMIAQ
CFOP_MFG=BQACAgUAAxkBAAIB3WEOVHKeYrrGhFo-GffB0W-tQRKlAALQAwACWy55VGny8ArGMkfoIAQ
CFOP_YX=BQACAgUAAxkBAAIB32EOVISFQbgmir2abj6QkgqaSX1WAALRAwACWy55VMEuU9lCYTYWIAQ

37
functions/Curl.php

@ -0,0 +1,37 @@
<?php
class Curl { // Curl模拟http请求
public $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.67';
public function get($url, $timeOut = 30) { // curl模拟Get 默认30s超时
return $this->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;
}
}
?>

72
functions/DNS.php

@ -0,0 +1,72 @@
<?php
class DNS { // DNS解析功能
public function resolveA($domain) { // DNS解析A记录
$ipAddr = array();
$rs = dns_get_record(strtolower($domain), DNS_A);
foreach ($rs as $record) {
$ipAddr[] = ip2long($record['ip']);
}
sort($ipAddr); // 解析结果排序
foreach ($ipAddr as &$ip) {
$ip = long2ip($ip);
}
return $ipAddr;
}
public function resolveAAAA($domain) { // DNS解析AAAA记录
$ipAddr = array();
$rs = dns_get_record(strtolower($domain), DNS_AAAA);
foreach ($rs as $record) {
$ipAddr[] = $this->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)));
}
}
?>

47
functions/DNSSEC.php

@ -0,0 +1,47 @@
<?php
class DNSSEC {
public function algorithmDesc($id) { // 获取算法类型
switch ($id) {
case '1':
return 'RSA/MD5';
case '3':
return 'DSA/SHA1';
case '5':
return 'RSA/SHA-1';
case '6':
return 'DSA-NSEC3-SHA1';
case '7':
return 'RSASHA1-NSEC3-SHA1';
case '8':
return 'RSA/SHA-256';
case '10':
return 'RSA/SHA-512';
case '12':
return 'GOST R 34.10-2001';
case '13':
return 'ECDSA Curve P-256 with SHA-256';
case '14':
return 'ECDSA Curve P-384 with SHA-384';
case '15':
return 'Ed25519';
case '16':
return 'Ed448';
}
}
public function digestDesc($id) { // 获取摘要类型
switch ($id) {
case '1':
return 'SHA-1';
case '2':
return 'SHA-256';
case '3':
return 'GOST R 34.11-94';
case '4':
return 'SHA-384';
}
}
}
?>

63
functions/ExtractDomain.php → functions/Domain.php

@ -1,9 +1,25 @@
<?php <?php
class extractDomain { class Domain { // 域名相关功能
private $tldDB = './db/tldInfo.db'; // 顶级域名数据库 private $tldDB = './db/tldInfo.db'; // 顶级域名数据库
public 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);
}
public 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)) { // IP地址
return true;
}
return false;
}
private function getAllTlds() { // 获取所有顶级域 含次级域 private function getTldList() { // 获取所有顶级域 含次级域
$db = new SqliteDB($this->tldDB); $db = new SqliteDB($this->tldDB);
$res = $db->query('SELECT record FROM `list`;'); $res = $db->query('SELECT record FROM `list`;');
while ($row = $res->fetchArray(SQLITE3_ASSOC)) { while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
@ -12,28 +28,21 @@ class extractDomain {
return $tlds; // Unicode字符使用Punycode编码 return $tlds; // Unicode字符使用Punycode编码
} }
private function isDomain($domain) { // 检测是否为域名 private function extractDomain($url) { // 从URL获取域名
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获取域名
$url = preg_replace('/^[\w]+:\/\//', '', $url); // 去除协议字段 $url = preg_replace('/^[\w]+:\/\//', '', $url); // 去除协议字段
$url = explode('?', $url)[0]; // 去除请求参数内容 $url = explode('?', $url)[0]; // 去除请求参数内容
$domain = explode('/', $url)[0]; // 分离域名 $domain = explode('/', $url)[0]; // 分离域名
return (new Punycode)->encode($domain); return (new Punycode)->encode($domain);
} }
private function getTld($domain) { // 搜索域名TLD private function getTld($domain) { // 匹配域名TLD
$tlds = $this->getAllTlds(); // 获取TLD列表 $tlds = $this->getTldList(); // 获取TLD列表
foreach ($tlds as $tld) { foreach ($tlds as $tld) {
if (substr($domain, -strlen($tld)) === $tld) { // 匹配测试 if (substr($domain, -strlen($tld)) === $tld) { // 匹配测试
$target[] = $tld; $target[] = $tld;
} }
} }
if (count($target) === 0) { if (!isset($target)) { return null; } // 匹配不到TLD
return ''; // 匹配不到TLD
};
$type = 0; $type = 0;
foreach ($target as $tld) { // 遍历可能的结果 foreach ($target as $tld) { // 遍历可能的结果
$num = substr_count($tld, '.'); $num = substr_count($tld, '.');
@ -52,15 +61,13 @@ class extractDomain {
} }
public function analyse($url) { // 分析域名信息 public function analyse($url) { // 分析域名信息
$domain = $this->getDomain($url); $domain = $this->extractDomain($url);
if (!$this->isDomain($domain)) { // 域名不合格 if (!$this->isDomain($domain)) { // 域名不合格
return array(); return array();
} }
$tld = $this->getTld($domain); $tld = $this->getTld($domain);
if ($tld == '') { // 匹配不到TLD if ($tld === null) { // 匹配不到TLD
return array( return [ 'domain' => $domain ];
'domain' => $domain
);
} }
return array( return array(
'domain' => $domain, 'domain' => $domain,
@ -68,6 +75,26 @@ class extractDomain {
'site' => $this->getSite($domain, $tld) '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;
}
} }
?> ?>

10
functions/RedisCache.php

@ -1,6 +1,6 @@
<?php <?php
class redisCache { class RedisCache {
private $redisSetting = array(); // Redis接口参数 private $redisSetting = array(); // Redis接口参数
public function __construct($prefix) { // 初始化 public function __construct($prefix) { // 初始化
@ -13,7 +13,7 @@ class redisCache {
); );
} }
public function getData($key) { // 查询Redis缓存不存在返回NULL public function getData($key) { // 查询Redis缓存 不存在返回NULL
$redis = new Redis(); $redis = new Redis();
$redis->connect($this->redisSetting['host'], $this->redisSetting['port']); $redis->connect($this->redisSetting['host'], $this->redisSetting['port']);
if ($this->redisSetting['passwd'] !== '') { if ($this->redisSetting['passwd'] !== '') {
@ -24,7 +24,7 @@ class redisCache {
return $redisValue; return $redisValue;
} }
public function setData($key, $data, $cacheTTL = 600) { // 写入信息到Redis缓存 默认10min过期 public function setData($key, $data, $cacheTTL = 0) { // 写入Redis缓存 默认不过期
$redis = new Redis(); $redis = new Redis();
$redis->connect($this->redisSetting['host'], $this->redisSetting['port']); $redis->connect($this->redisSetting['host'], $this->redisSetting['port']);
if ($this->redisSetting['passwd'] !== '') { if ($this->redisSetting['passwd'] !== '') {
@ -32,7 +32,9 @@ class redisCache {
} }
$redisKey = $this->redisSetting['prefix'] . $key; $redisKey = $this->redisSetting['prefix'] . $key;
$status = $redis->set($redisKey, $data); // 写入数据库 $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; return $status;
} }
} }

4
functions/SqliteDB.php

@ -2,10 +2,10 @@
class SqliteDB extends SQLite3 { // Sqlite3数据库 class SqliteDB extends SQLite3 { // Sqlite3数据库
public function __construct($filename) { public function __construct($filename) {
$this->open($filename); $this->open($filename); // 打开数据库连接
} }
public function __destruct() { public function __destruct() {
$this->close(); $this->close(); // 关闭数据库连接
} }
} }

10
main.php

@ -2,17 +2,20 @@
require_once 'env.php'; require_once 'env.php';
require_once 'route.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/Punycode.php';
require_once 'functions/SqliteDB.php'; require_once 'functions/SqliteDB.php';
require_once 'functions/RedisCache.php'; require_once 'functions/RedisCache.php';
require_once 'functions/TgInterface.php'; require_once 'functions/TgInterface.php';
require_once 'functions/ExtractDomain.php';
fastcgi_finish_request(); // 断开请求连接
fastcgi_finish_request(); // 断开连接
$env = loadEnv('.env'); // 载入环境变量 $env = loadEnv('.env'); // 载入环境变量
$apiToken = $env['BOT_TOKEN']; $apiToken = $env['BOT_TOKEN'];
$botAccount = $env['BOT_NAME']; // 机器人用户名 $botAccount = $env['BOT_NAME']; // 机器人用户名
ini_set('date.timezone', $env['TIME_ZONE']); // 设置时区
$apiPath = 'https://api.telegram.org/bot' . $apiToken; // Telegram API接口 $apiPath = 'https://api.telegram.org/bot' . $apiToken; // Telegram API接口
$webhook = json_decode(file_get_contents("php://input"), TRUE); // Webhook接受信息 $webhook = json_decode(file_get_contents("php://input"), TRUE); // Webhook接受信息
@ -27,6 +30,7 @@ if ($isCallback) { // 回调请求模式
$messageFrom = $webhook['message']['from']; $messageFrom = $webhook['message']['from'];
} }
$chat = $message['chat']; $chat = $message['chat'];
$tgEnv = array( $tgEnv = array(
'isGroup' => ($chat['type'] === 'group') ? true : false, // 是否为群组 'isGroup' => ($chat['type'] === 'group') ? true : false, // 是否为群组
'isCallback' => $isCallback, // 是否为回调请求 'isCallback' => $isCallback, // 是否为回调请求

123
models/cfopPic.php

@ -1,91 +1,74 @@
<?php <?php
class cfopPicEntry { class cfopPicEntry { // 获取CFOP公式图片
private function getCfopMsg() { private function sendMenu() { // 发送菜单
return array( $buttons = 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'
)
)
);
tgApi::sendMessage(array(
'text' => 'CFOP魔方公式合集', 'text' => 'CFOP魔方公式合集',
'reply_markup' => json_encode(array( 'reply_markup' => json_encode(array(
'inline_keyboard' => array( 'inline_keyboard' => $buttons
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'
)
)
)
)) ))
); ));
} }
private function getPicId($type) { // 返回图片文件ID private function sendCfopPic($type) { // 发送图片
global $env; global $env;
switch ($type) { if ($type === 'gan') {
case 'gan': $picId = $env['CFOP_GAN'];
return $env['CFOP_GAN']; } else if ($type === 'mfg') {
case 'mfg': $picId = $env['CFOP_MFG'];
return $env['CFOP_MFG']; } else if ($type === 'yx') {
case 'yx': $picId = $env['CFOP_YX'];
return $env['CFOP_YX']; } else if ($type === '' || $type === 'help') {
} $this->sendMenu();
}
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);
} else { } else {
tgApi::sendMessage($params); tgApi::sendText('未知公式');
}
if (isset($picId)) {
tgApi::sendDocument(array(
'document' => $picId
));
} }
} }
public function query($rawParam) { // CFOP图片查询入口 public function query($rawParam) { // CFOP图片查询入口
$this->sendCfopPic($this->getPic($rawParam)); $this->sendCfopPic($rawParam);
} }
public function callback($rawParam) { // CFOP图片回调入口 public function callback($rawParam) { // CFOP图片回调入口
if ($rawParam === 'all') { if ($rawParam !== 'all') {
global $tgEnv; $this->sendCfopPic($rawParam);
tgApi::deleteMessage(array( // 删除源消息
'message_id' => $tgEnv['messageId']
));
$this->sendCfopPic($this->getPic('gan'));
$this->sendCfopPic($this->getPic('mfg'));
$this->sendCfopPic($this->getPic('yx'));
return; return;
} }
$this->sendCfopPic($this->getPic($rawParam)); $this->sendCfopPic('gan');
$this->sendCfopPic('mfg');
$this->sendCfopPic('yx');
tgApi::deleteMessage(array( // 删除源消息
'message_id' => $GLOBALS['tgEnv']['messageId']
));
} }
} }

208
models/icpQuery.php

@ -1,161 +1,56 @@
<?php <?php
class icpQuery { class icpQuery { // ICP备案查询
private $apiPath; private $apiPath;
public function __construct() { public function __construct() {
global $env; $this->apiPath = 'https://apidatav2.chinaz.com/single/icp?key=' . $GLOBALS['env']['ICP_KEY'] . '&domain=';
$this->apiPath = $env['ICP_API'] . '?key=' . $env['ICP_KEY'] . '&domain=';
} }
private function getIcpInfo($domain) { // ICP备案查询 private function getIcpInfo($domain) { // ICP备案查询
$domain = urlencode((new Punycode)->decode($domain)); $domain = urlencode((new Punycode)->decode($domain)); // API接口需要URL编码原始域名
$info = json_decode(file_get_contents($this->apiPath . $domain), true); $info = json_decode(file_get_contents($this->apiPath . $domain), true); // 请求API接口
if ($info['StateCode'] === 1) { // 存在ICP备案 if ($info['StateCode'] === 1) {
$info = array( return $info['Result'] + array( // 存在ICP备案
'status' => 'ok', 'time' => time(),
'hasIcp' => true '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 { } else if ($info['StateCode'] === 0) {
$info = array( if ($info['Reason'] !== '暂无备案信息') { return null; } // 服务错误
'status' => 'error', // 服务错误 return array( // 无ICP备案
'message' => 'Server error' 'time' => time(),
'hasIcp' => false
); );
} else {
return null; // 服务错误
} }
return $info;
} }
public function isCache($domain) { // 查询域名是否存在缓存 public function isCache($domain) { // 查询域名是否存在缓存
$redis = new redisCache('icp'); $redis = new RedisCache('icp');
$info = $redis->getData($domain); // 查询缓存数据 $info = $redis->getData($domain); // 查询缓存数据
return ($info) ? true : false; return ($info) ? true : false;
} }
public function icpInfo($domain) { // ICP查询入口 带缓存 public function icpInfo($domain, $isCache = true) { // ICP查询入口 默认带缓存
$redis = new redisCache('icp'); $redis = new RedisCache('icp');
$info = $redis->getData($domain); // 查询缓存数据 $info = $redis->getData($domain); // 查询缓存数据
if (!$info) { // 缓存未命中 if (!$isCache || !$info) { // 不缓存 或 缓存未命中
$info = $this->getIcpInfo($domain); // 执行查询 $info = $this->getIcpInfo($domain); // 发起查询
if ($info['status'] !== 'ok') { // 查询错误 if ($info === null) { return null; } // 服务错误
return $info; $redis->setData($domain, json_encode($info)); // 缓存ICP信息 永久
}
unset($info['status']);
$redis->setData($domain, json_encode($info), 90 * 86400); // 缓存90day
} else { // 缓存命中 } else { // 缓存命中
$info = json_decode($info, true); // 使用缓存数据 $info = json_decode($info, true); // 使用缓存数据
} }
return array( return $info;
'status' => 'ok'
) + $info;
} }
} }
class icpQueryEntry { class icpQueryEntry { // ICP信息查询入口
private function getIcpTlds() { // 获取所有可ICP备案的顶级域 private function genMessage($domain, $info) { // 生成ICP数据消息
$db = new SqliteDB('./db/tldInfo.db'); if ($info === null) { return 'Server error'; } // 服务错误
$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'];
$msg = '`' . (new Punycode)->decode($domain) . '`' . PHP_EOL; $msg = '`' . (new Punycode)->decode($domain) . '`' . PHP_EOL;
if (!(new icpQuery)->isCache($domain)) { // 域名信息未缓存 if (!$info['hasIcp']) { return $msg . '暂无备案信息'; } // 无ICP备案
$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;
}
$msg .= '*类型:*' . $info['CompanyType'] . PHP_EOL; $msg .= '*类型:*' . $info['CompanyType'] . PHP_EOL;
if ($info['Owner'] != '') { // 负责人为空不显示 if ($info['Owner'] != '') { // 负责人为空不显示
$msg .= '*负责人:*' . $info['Owner'] . PHP_EOL; $msg .= '*负责人:*' . $info['Owner'] . PHP_EOL;
@ -172,16 +67,53 @@ class icpQueryEntry {
$msg .= '*网站名:*' . $info['SiteName'] . PHP_EOL; $msg .= '*网站名:*' . $info['SiteName'] . PHP_EOL;
$msg .= '*审核时间:*' . $info['VerifyTime'] . PHP_EOL; $msg .= '*审核时间:*' . $info['VerifyTime'] . PHP_EOL;
$msg .= '*许可证号:*' . $info['SiteLicense'] . PHP_EOL; $msg .= '*许可证号:*' . $info['SiteLicense'] . PHP_EOL;
if ($isCache) { // 没有缓冲信息 直接发送 return $msg;
tgApi::sendMarkdown($msg); // 返回查询数据 }
} else {
tgApi::editMessage(array( // 返回查询数据 修改原消息 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', 'parse_mode' => 'Markdown',
'text' => $msg, 'message_id' => $messageId, // 替换原消息
'message_id' => $message['result']['message_id'] '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']); // 查询并输出域名备案信息
} }
} }
} }

176
models/ipInfo.php

@ -1,11 +1,10 @@
<?php <?php
class ipInfo { class ipInfo { // 查询IP详细信息
private $apiPath; private $apiPath;
public function __construct() { public function __construct() {
global $env; $this->apiPath = 'https://' . $GLOBALS['env']['ECHOIP_HOST'] . '/info/';
$this->apiPath = $env['ECHOIP_API'];
} }
private function changeCoor($num) { // 转为时分秒格式 private function changeCoor($num) { // 转为时分秒格式
@ -27,83 +26,32 @@ class ipInfo {
return $str; return $str;
} }
public function getInfo($ip) { private function getIpInfo($ip) { // 向上游查询IP信息
if (!filter_var($ip, FILTER_VALIDATE_IP)) { // IP地址不合法 $content = (new Curl)->get($this->apiPath . $ip);
return array( $info = json_decode($content, true);
'status' => 'error', if ($info['status'] !== 'T') { return null; }
'message' => 'Illegal IP Address' unset($info['status']);
); return $info + array(
} 'locCoor' => $this->coorFormat($info['loc']) // 经纬度格式
$redis = new redisCache('ip'); );
}
public function getInfo($ip) { // 查询IP信息 错误返回null
$redis = new RedisCache('ip');
$info = $redis->getData($ip); // 查询缓存数据 $info = $redis->getData($ip); // 查询缓存数据
if (!$info) { // 缓存未命中 if (!$info) { // 缓存未命中
$info = json_decode(file_get_contents($this->apiPath . $ip), true); $info = $this->getIpInfo($ip);
if ($info['status'] !== 'T') { // 上游接口错误 if ($info === null) { return null; } // 服务错误 返回null
return array( $redis->setData($ip, json_encode($info), 43200); // 缓存12h
'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
} else { // 缓存命中 } else { // 缓存命中
$info = json_decode($info, true); // 使用缓存数据 $info = json_decode($info, true); // 使用缓存数据
} }
return array( return $info; // 查询成功 返回结果
'status' => 'ok',
'data' => json_encode($info) // 返回查询结果
);
} }
} }
class ipInfoEntry { class ipInfoEntry { // IP信息查询入口
private function isDomain($domain) { // 检测是否为域名 private function genMessage($info) { // 生成返回信息
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);
$msg = '<b>IP:</b> <code>' . $info['ip'] . '</code>' . PHP_EOL; $msg = '<b>IP:</b> <code>' . $info['ip'] . '</code>' . PHP_EOL;
if ($info['as'] != NULL) { if ($info['as'] != NULL) {
$msg .= '<b>AS:</b> <a href="https://bgpview.io/asn/' . substr($info['as'], 2) . '">'; $msg .= '<b>AS:</b> <a href="https://bgpview.io/asn/' . substr($info['as'], 2) . '">';
@ -122,43 +70,37 @@ class ipInfoEntry {
if ($info['scope'] != NULL) { $msg .= '<b>Scope:</b> <code>' . $info['scope'] . '</code>' . PHP_EOL; } if ($info['scope'] != NULL) { $msg .= '<b>Scope:</b> <code>' . $info['scope'] . '</code>' . PHP_EOL; }
if ($info['detail'] != NULL) { $msg .= '<b>Detail:</b> ' . $info['detail'] . PHP_EOL; } if ($info['detail'] != NULL) { $msg .= '<b>Detail:</b> ' . $info['detail'] . PHP_EOL; }
return array( return array(
'text' => $msg,
'parse_mode' => 'HTML', // HTML格式输出 'parse_mode' => 'HTML', // HTML格式输出
'disable_web_page_preview' => 'true', // 不显示页面预览 'disable_web_page_preview' => 'true', // 不显示页面预览
'text' => $msg, 'reply_markup' => $this->genButton('View on echoIP', $info['ip']) // 显示按钮
'reply_markup' => json_encode(array( // 显示按钮
'inline_keyboard' => array([[
'text' => 'View on echoIP',
'url' => 'https://ip.dnomd343.top/?ip=' . $ip
]])
))
); );
} }
public function query($rawParam) { // ipInfo查询入口 private function genButton($text, $ip = '') { // 生成ehcoIP页面链接按钮 默认为主页
if ($rawParam == '' || $rawParam === 'help') { // 显示使用说明 $url = 'https://' . $GLOBALS['env']['ECHOIP_HOST'] . '/';
tgApi::sendMessage(array( if ($ip !== '') { $url .= '?ip=' . $ip; }
'parse_mode' => 'Markdown', return json_encode(array(
'text' => '*Usage:* `/ip IPv4/IPv6/Domain`', 'inline_keyboard' => array([[ // echoIP按钮
'reply_markup' => json_encode(array( // 显示echoIP按钮 'text' => $text,
'inline_keyboard' => array([[ 'url' => $url
'text' => 'Get my IP address', ]])
'url' => 'https://ip.dnomd343.top/' ));
]]) }
))
)); private function sendInfo($ip) { // 查询并发送IP信息
return; $info = (new ipInfo)->getInfo($ip);
} if ($info === null) {
if (filter_var($rawParam, FILTER_VALIDATE_IP)) { // 参数为IP地址 tgApi::sendText('Server error'); // 上游查询错误
tgApi::sendMessage($this->getInfo($rawParam)); // 发起查询
return;
}
if (!$this->isDomain($rawParam)) {
tgApi::sendText('Illegal Request'); // 非法请求
return; return;
} }
$ips = $this->dnsResolve($rawParam); tgApi::sendMessage($this->genMessage($info));
}
private function sendDomainInfo($domain) { // 查询并发送域名解析结果
$ips = (new DNS)->resolveIP($domain);
if (count($ips) == 0) { // 解析不到IP记录 if (count($ips) == 0) { // 解析不到IP记录
tgApi::sendMarkdown('Nothing found of `' . $rawParam . '`'); tgApi::sendMarkdown('Nothing found of `' . $domain . '`');
return; return;
} }
foreach ($ips as $ip) { foreach ($ips as $ip) {
@ -170,27 +112,47 @@ class ipInfoEntry {
if (count($ips) >= 2) { if (count($ips) >= 2) {
$buttons[] = array([ // 两个及以上的IP 添加显示全部的按钮 $buttons[] = array([ // 两个及以上的IP 添加显示全部的按钮
'text' => 'Get all detail', 'text' => 'Get all detail',
'callback_data' => '/ip ' . $rawParam 'callback_data' => '/ip ' . $domain
]); ]);
} }
tgApi::sendMessage(array( tgApi::sendMessage(array(
'parse_mode' => 'Markdown', 'parse_mode' => 'Markdown',
'text' => 'DNS resolve of `' . $rawParam . '`', 'text' => 'DNS resolve of `' . $domain . '`',
'reply_markup' => json_encode(array( // 显示IP列表按钮 'reply_markup' => json_encode(array( // IP列表按钮
'inline_keyboard' => $buttons '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回调入口 public function callback($rawParam) { // ipInfo回调入口
if (filter_var($rawParam, FILTER_VALIDATE_IP)) { // 参数为IP地址 if (filter_var($rawParam, FILTER_VALIDATE_IP)) { // 参数为IP地址
$this->query($rawParam); $this->query($rawParam);
} else { // 参数为域名 } else { // 参数为域名
global $tgEnv;
tgApi::deleteMessage(array( 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) { foreach ($ips as $ip) {
$this->query($ip); // 逐个输出 $this->query($ip); // 逐个输出
} }

354
models/kmsCheck.php

@ -1,16 +1,16 @@
<?php <?php
class kmsKeys { class kmsKeys { // KMS密钥获取
private $kmsDB = './db/kmsKeys.db'; // KMS密钥数据库 private $db, $kmsDB = './db/kmsKeys.db'; // KMS密钥数据库
private function getVersionName($type, $version_id) { // 获取对应版本的名称
$db = new SqliteDB($this->kmsDB); private function getVersionName($type, $versionId) { // 获取对应版本的名称
$res = $db->query('SELECT * FROM `' . $type . '_version` WHERE version_id=' . $version_id . ';'); $res = $this->db->query('SELECT * FROM `' . $type . '_version` WHERE version_id=' . $versionId . ';');
return $res->fetchArray(SQLITE3_ASSOC)['version_name']; return $res->fetchArray(SQLITE3_ASSOC)['version_name'];
} }
private function getKmsKeys($type) { // 获取所有版本的KMS密钥 private function getKmsKeys($type) { // 获取指定类型的密钥信息 win/win-server
$db = new SqliteDB($this->kmsDB); $this->db = new SqliteDB($this->kmsDB);
$res = $db->query('SELECT * FROM `' . $type . '`;'); $res = $this->db->query('SELECT * FROM `' . $type . '`;');
while ($row = $res->fetchArray(SQLITE3_ASSOC)) { while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
$index = $row['version']; $index = $row['version'];
unset($row['version']); unset($row['version']);
@ -19,110 +19,101 @@ class kmsKeys {
return $data; return $data;
} }
public function getKeys($type) { // 获取指定类型KMS密钥 public function getKeys($type) { // 获取指定类型的各版本及KMS密钥
switch ($type) { switch ($type) {
case '': case '': // win和win-server系列
return $this->getKmsKeys('win') + $this->getKmsKeys('win-server'); return $this->getKmsKeys('win') + $this->getKmsKeys('win-server');
case 'win': case 'win': // win系列
return $this->getKmsKeys('win'); return $this->getKmsKeys('win');
case 'win-server': case 'win-server': // win-server系列
return $this->getKmsKeys('win-server'); return $this->getKmsKeys('win-server');
default: default:
return array(); return array(); // 未知类型 返回空数组
} }
} }
} }
class kmsCheck { class kmsCheck { // KMS服务器检查
private $api; 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() { public function isCache($server) { // 检查KMS服务器是否已缓存
global $env; $redis = new RedisCache('kms');
$this->api = $env['KMS_API']; $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; $server = $host . ':' . $port;
$redis = new redisCache('kms'); $status = $redis->getData($server);
$info = $redis->getData($server); // 查询缓存数据 if (!$status) { // 缓存未命中
if (!$info) { // 缓存未命中 if ($this->getKmsStatus($host, $port)) { // 测试服务器状态
$url = $this->api . 'check?host=' . $host . '&port=' . $port; $status = [ 'status' => 'online' ]; // 服务正常
$info = json_decode(file_get_contents($url), true); // 请求上游接口 } else {
$info['server'] = $server; $status = [ 'status' => 'offline' ]; // 服务掉线
switch ($info['status']) {
case 'ok':
$info['online'] = true;
break;
case 'error':
$info['online'] = false;
break;
default:
return array(
'status' => 'error',
'message' => 'Server error'
);
} }
$info['status'] = 'ok'; $redis->setData($server, json_encode($status), 300); // 缓存5min
unset($info['message']);
$redis->setData($server, json_encode($info), 300); // 缓存5min
} else { // 缓存命中 } else { // 缓存命中
$info = json_decode($info, true); // 使用缓存数据 $status = json_decode($status, true); // 使用缓存数据
} }
return $info; return $status;
} }
} }
class kmsCheckEntry { class kmsCheckEntry { // KMS功能入口
private function isHost($host) { // 判断host是否合法 private function formatCheck($server) { // 输入参数格式检查
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) {
$temp = explode(':', $server); $temp = explode(':', $server);
if (count($temp) === 1) { // 不带:的请求 if (count($temp) === 1) { // 不带:的请求
if ($this->isHost($temp[0])) { if (!(new Domain)->isHost($temp[0])) { return null; } // 错误请求
$host = $server; return array(
$port = 1688; 'host' => $temp[0],
} else { 'port' => 1688
return array( );
'status' => 'error',
'message' => 'Illegal host'
);
}
} else if (count($temp) === 2) { // 带一个:的请求 } else if (count($temp) === 2) { // 带一个:的请求
if ($this->isHost($temp[0])) { if (!(new Domain)->isHost($temp[0])) { return null; } // 错误请求
$host = $temp[0]; if ($temp[1] < 0 || $temp[1] > 65535) { return null; } // 错误请求
} else {
return array(
'status' => 'error',
'message' => 'Illegal host'
);
}
$port = $temp[1];
if ($port < 0 || $port > 65535) {
return array(
'status' => 'error',
'message' => 'Illegal port'
);
}
} else { // 带多个:的请求
return array( return array(
'status' => 'error', 'host' => $temp[0],
'message' => 'Illegal request' 'port' => $temp[1]
); );
} else { // 带多个:的请求
return null; // 错误请求
} }
return array( }
'status' => 'ok',
'host' => $host, private function genMessage($server, $status) { // 生成KMS状态消息
'port' => $port $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) { // 简化版本名称 private function simpStr($str) { // 简化版本名称
@ -130,17 +121,41 @@ class kmsCheckEntry {
return implode('', $match[0]); 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); $kmsKeys = (new kmsKeys)->getKeys($type);
foreach ($kmsKeys as $version => $kmsKey) { foreach ($kmsKeys as $version => $kmsKey) {
$buttons[] = array([ // 生成按钮列表 $buttons[] = array([ // 生成按钮列表
'text' => $version, 'text' => $version,
'callback_data' => '/kms ' . $this->simpStr($version) 'callback_data' => '/kms ' . $type . '|' . $this->simpStr($version)
]); ]);
} }
$buttons[] = array([ $buttons[] = array([
'text' => '<< Go back <<', 'text' => '<< Go back <<',
'callback_data' => '/kms keys' 'callback_data' => '/kms menu' // 加入返回按钮
]); ]);
return array( return array(
'text' => 'Which version did you need?', 'text' => 'Which version did you need?',
@ -150,122 +165,91 @@ class kmsCheckEntry {
); );
} }
private function getKmsKeys($targetVersion) { // 显示指定版本的KMS密钥列表 private function genActiveCmd() { // 生成KMS激活命令
$kmsKeys = (new kmsKeys)->getKeys(''); $kmsHost = $GLOBALS['env']['KMS_HOST'];
foreach ($kmsKeys as $version => $kmsKey) { // 比对压缩以后的名称 $actiCmd = '```' . PHP_EOL . 'slmgr /upk' . PHP_EOL . 'slmgr /ipk {KMS_KEY}' . PHP_EOL;
if ($this->simpStr($version) === $targetVersion) { break; } // 匹配成功 $actiCmd .= 'slmgr /skms ' . $kmsHost . PHP_EOL . 'slmgr /ato' . PHP_EOL . 'slmgr /dlv';
} $actiCmd .= PHP_EOL . '```';
$msg = '*' . $version . ' KMS Keys*' . PHP_EOL . PHP_EOL;
foreach ($kmsKey as $row) {
$msg .= $row['name'] . ':`' . $row['key'] . '`' . PHP_EOL . PHP_EOL;
}
return array( return array(
'text' => $actiCmd,
'parse_mode' => 'Markdown', '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服务器状态 private function genSelectMsg() { // 生成KMS版本选择消息
$content = (new kmsCheck)->kmsStatus($host, $port); return array(
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(
'text' => 'Which one did you need?', 'text' => 'Which one did you need?',
'reply_markup' => json_encode(array( 'reply_markup' => json_encode(array(
'inline_keyboard' => array( 'inline_keyboard' => array( // 功能选择按钮
array([ array([
'text' => 'Windows', 'text' => 'Windows',
'callback_data' => '/kms win' 'callback_data' => '/kms win' // Windows密钥
]), ]),
array([ array([
'text' => 'Windows Server', 'text' => 'Windows Server',
'callback_data' => '/kms win-server' 'callback_data' => '/kms win-server' // Windows Server密钥
]), ]),
array([ array([
'text' => 'Activation Command', '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) { switch ($rawParam) {
case 'keys': case 'menu': // 选择菜单
tgApi::editMessage(array( $message = $this->genSelectMsg();
'message_id' => $tgEnv['messageId'], break;
) + $selectMsg); case 'cmd': // KMS激活命令
return; $message = $this->genActiveCmd();
case 'cmd': break;
tgApi::editMessage(array( case 'win': // Windows激活密钥
'message_id' => $tgEnv['messageId'], case 'win-server': // Windows Server激活密钥
'parse_mode' => 'Markdown', $message = $this->genKmsVersions($rawParam);
'text' => $actiCmd, break;
'reply_markup' => json_encode(array( default:
'inline_keyboard' => array([[ $message = $this->genKmsKeys($rawParam); // 显示密钥列表
'text' => '<< Go back <<',
'callback_data' => '/kms keys'
]])
))
));
return;
case 'win':
case 'win-server':
tgApi::editMessage(array(
'message_id' => $tgEnv['messageId'],
) + $this->getKmsVersions($rawParam));
return;
} }
tgApi::editMessage(array( tgApi::editMessage(array(
'message_id' => $tgEnv['messageId'] 'message_id' => $messageId // 修改源消息内容
) + $this->getKmsKeys($rawParam)); ) + $message);
} }
} }

213
models/ntpCheck.php

@ -1,28 +1,27 @@
<?php <?php
class ntpList { class ntpServer { // 获取NTP服务器列表
private $ntpDB = './db/ntpServer.db'; // NTP服务器数据库 private $db, $ntpDB = './db/ntpServer.db'; // NTP服务器数据库
private function getListName($list_id) { // 获取对应组的名称 private function getListName($listId) { // 获取对应组的名称
$db = new SqliteDB($this->ntpDB); $res = $this->db->query('SELECT * FROM `ntp_list` WHERE id=' . $listId . ';');
$res = $db->query('SELECT * FROM `ntp_list` WHERE id=' . $list_id . ';');
return $res->fetchArray(SQLITE3_ASSOC)['name']; return $res->fetchArray(SQLITE3_ASSOC)['name'];
} }
public function getNtpList() { // 获取所有NTP服务器地址 public function getList() { // 获取所有NTP服务器地址
$db = new SqliteDB($this->ntpDB); $this->db = new SqliteDB($this->ntpDB);
$res = $db->query('SELECT * FROM `ntp_host`;'); $res = $this->db->query('SELECT * FROM `ntp_host`;');
while ($row = $res->fetchArray(SQLITE3_ASSOC)) { while ($row = $res->fetchArray(SQLITE3_ASSOC)) {
$index = $row['list_id']; $index = $row['list_id'];
unset($row['list_id']); unset($row['list_id']);
$data[$this->getListName($index)][] = $row; $list[$this->getListName($index)][] = $row;
} }
return $data; return $list;
} }
} }
class ntpCheck { class ntpCheck { // NTP服务器检查
private function formatOffset($str) { // 格式化Offset private function formatOffset($str) { // 格式化偏移时间
$num = number_format($str, 6) * 1000; // s -> ms $num = number_format($str, 6) * 1000; // s -> ms
$str = sprintf("%1\$.3f", $num); // 补零到小数点后3位 $str = sprintf("%1\$.3f", $num); // 补零到小数点后3位
if ($num > 0) { if ($num > 0) {
@ -31,23 +30,61 @@ class ntpCheck {
return $str . 'ms'; return $str . 'ms';
} }
private function curlPost($url, $data) { // curl模拟post操作 40s超时 private function sortByIp($servers) { // 排序算法
$curl = curl_init(); $temp = array();
curl_setopt($curl, CURLOPT_URL, $url); foreach ($servers as $val){
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $temp[] = $val['Server'];
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 40); }
curl_setopt($curl, CURLOPT_POST, 1); sort($temp);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data); $temp = array_flip($temp);
$content = curl_exec($curl); $sort = array();
curl_close($curl); foreach ($servers as $val) {
return $content; $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服务器状态 private function getNtpStatus($host) { // 获取NTP服务器状态
$html = $this->curlPost('https://servertest.online/ntp', array( $html = (new Curl)->post('https://servertest.online/ntp', array(
'a' => $host, 'a' => $host,
'c' => 'Query+both' 'c' => 'Query+both'
)); ));
if ($html == '') { return null; } // 服务错误
preg_match('/<\/form>[\s\S]+<footer>/', $html, $match); // 切取数据部分 preg_match('/<\/form>[\s\S]+<footer>/', $html, $match); // 切取数据部分
preg_match('/<label>[\s\S]+<footer>/', $match[0], $match); // 去除前部干扰 preg_match('/<label>[\s\S]+<footer>/', $match[0], $match); // 去除前部干扰
$match = substr($match[0], 0, strlen($match[0]) - 8); // 去除后部<footer> $match = substr($match[0], 0, strlen($match[0]) - 8); // 去除后部<footer>
@ -83,15 +120,23 @@ class ntpCheck {
$group['Offset'] = $this->formatOffset($group['Offset']); // 转换为ms $group['Offset'] = $this->formatOffset($group['Offset']); // 转换为ms
$data[] = $group; $data[] = $group;
} }
return ($data === null) ? array() : $data; $data = (!isset($data)) ? array() : $data; // 无结果时为空数组
return $this->sortServer($data); // 排序后返回
}
public function isCache($host) { // 检查NTP服务器是否已缓存
$redis = new RedisCache('ntp');
$servers = $redis->getData($host);
return (!$servers) ? false : true;
} }
public function ntpStatus($host) { // 检测NTP服务器状态 带缓存 public function ntpStatus($host) { // 检测NTP服务器状态
$redis = new redisCache('ntp'); $redis = new RedisCache('ntp');
$servers = $redis->getData($host); // 查询缓存数据 $servers = $redis->getData($host); // 查询缓存数据
if (!$servers) { // 缓存未命中 if (!$servers) { // 缓存未命中
$servers = $this->getNtpStatus($host); // 发起测试 $servers = $this->getNtpStatus($host); // 发起测试
$redis->setData($host, json_encode($servers), 300); // 缓存5min if ($servers === null) { return null; } // 服务接口错误
$redis->setData($host, json_encode($servers), 900); // 缓存15min
} else { // 缓存命中 } else { // 缓存命中
$servers = json_decode($servers, true); // 使用缓存数据 $servers = json_decode($servers, true); // 使用缓存数据
} }
@ -99,24 +144,13 @@ class ntpCheck {
} }
} }
class ntpCheckEntry { class ntpCheckEntry { // NTP功能入口
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)) { // IP地址
return true;
}
return false;
}
private function hashGroupName($str) { // 计算组名的哈希值 取前12位 private function hashGroupName($str) { // 计算组名的哈希值 取前12位
return substr(md5($str), 0, 12); return substr(md5($str), 0, 12);
} }
private function showList() { // 列出所有NTP服务器组 private function showNtpList() { // 列出所有NTP服务器组
$ntpList = (new ntpList)->getNtpList(); $ntpList = (new ntpServer)->getList();
foreach ($ntpList as $index => $ntpHosts) { foreach ($ntpList as $index => $ntpHosts) {
$buttons[] = array([ // 生成按钮列表 $buttons[] = array([ // 生成按钮列表
'text' => $index, 'text' => $index,
@ -125,29 +159,29 @@ class ntpCheckEntry {
} }
return array( return array(
'text' => 'Which one did you like?', 'text' => 'Which one did you like?',
'reply_markup' => json_encode(array( // 列表按钮 'reply_markup' => json_encode(array( // 按钮列表
'inline_keyboard' => $buttons 'inline_keyboard' => $buttons
)) ))
); );
} }
private function showNtpServer($targetGroup) { // 列出指定组的NTP服务器 private function showNtpServer($targetGroup) { // 列出指定组的NTP服务器
$ntpList = (new ntpList)->getNtpList(); $ntpList = (new ntpServer)->getList();
foreach ($ntpList as $index => $ntpHosts) { foreach ($ntpList as $index => $ntpHosts) { // 搜索目标组
if ($this->hashGroupName($index) === $targetGroup) { break; } if ($this->hashGroupName($index) === $targetGroup) { break; }
} }
$msg = '*' . $index . '*' . PHP_EOL; $msg = '*' . $index . '*' . PHP_EOL; // 显示组名
foreach ($ntpHosts as $ntpHost) { foreach ($ntpHosts as $ntpHost) {
if ($ntpHost['desc'] !== '') { if ($ntpHost['desc'] !== '') {
$msg .= $ntpHost['desc'] . ':'; $msg .= $ntpHost['desc'] . ':'; // 服务器描述
} }
$msg .= '`' . $ntpHost['host'] . '`' . PHP_EOL; $msg .= '`' . $ntpHost['host'] . '`' . PHP_EOL; // 服务器地址
} }
return array( return array(
'parse_mode' => 'Markdown',
'text' => $msg, 'text' => $msg,
'parse_mode' => 'Markdown',
'reply_markup' => json_encode(array( 'reply_markup' => json_encode(array(
'inline_keyboard' => array([[ 'inline_keyboard' => array([[ // 返回上一级 按钮
'text' => ' << Go back <<', 'text' => ' << Go back <<',
'callback_data' => '/ntp servers' 'callback_data' => '/ntp servers'
]]) ]])
@ -155,65 +189,76 @@ class ntpCheckEntry {
); );
} }
private function checkNtp($host) { // 检查NTP服务器状态 private function genMessage($host, $servers) { // 生成返回信息
$servers = (new ntpCheck)->ntpStatus($host); if (count($servers) === 0) { // NTP服务不可用
if (count($servers) === 0) {
$msg = '`' . $host . '`' . PHP_EOL; $msg = '`' . $host . '`' . PHP_EOL;
$msg .= 'NTP Server *Offline*' . PHP_EOL . PHP_EOL; $msg .= 'NTP Server *Offline*' . PHP_EOL . PHP_EOL;
return array( return $msg;
'parse_mode' => 'Markdown',
'text' => $msg
);
} }
$msg = '`' . $host . '`' . PHP_EOL; $msg = '`' . $host . '`' . PHP_EOL;
$msg .= 'NTP Server *Normally*' . PHP_EOL . PHP_EOL; $msg .= 'NTP Server *Normally*' . PHP_EOL . PHP_EOL; // NTP服务器正常
foreach ($servers as $server) { foreach ($servers as $server) { // 显示所有服务器
$msg .= '`' . $server['Server'] . '`' . PHP_EOL; $msg .= '`' . $server['Server'] . '`' . PHP_EOL;
$msg .= '_Stratum:_ ' . $server['Stratum'] . PHP_EOL; $msg .= '_Stratum:_ ' . $server['Stratum'] . PHP_EOL;
$msg .= '_Offset:_ ' . $server['Offset'] . PHP_EOL; $msg .= '_Offset:_ ' . $server['Offset'] . PHP_EOL;
$msg .= PHP_EOL; $msg .= PHP_EOL;
} }
return array( return $msg;
}
private function sendNtpStatus($host) { // 检查并发送NTP服务器状态
if ((new ntpCheck)->isCache($host)) { // 状态已缓存
$servers = (new ntpCheck)->ntpStatus($host);
tgApi::sendMarkdown($this->genMessage($host, $servers)); // 发送服务器状态
return;
}
$message = tgApi::sendMarkdown('`' . $host . '`' . PHP_EOL . 'NTP Server Checking...');
$messageId = json_decode($message, true)['result']['message_id']; // 未缓存 发送缓冲消息
$servers = (new ntpCheck)->ntpStatus($host); // 发起查询
if ($servers === null) { // 上游接口错误
$message = 'Server error';
} else {
$message = $this->genMessage($host, $servers); // 生成返回信息
}
tgApi::editMessage(array( // 返回查询结果
'text' => $message,
'parse_mode' => 'Markdown',
'message_id' => $messageId
));
}
private function sendHelp() { // 显示使用说明
$helpMessage = array(
'parse_mode' => 'Markdown', 'parse_mode' => 'Markdown',
'text' => $msg 'text' => '*Usage:* `/ntp IP/Domain`',
'reply_markup' => json_encode(array( // 显示 NTP服务器列表 按钮
'inline_keyboard' => array([[
'text' => 'Get NTP Servers',
'callback_data' => '/ntp servers'
]])
))
); );
tgApi::sendMessage($helpMessage); // 发送使用说明
} }
public function query($rawParam) { // NTP测试查询入口 public function query($rawParam) { // NTP测试查询入口
if ($rawParam == '' || $rawParam === 'help') { // 显示使用说明 if ($rawParam == '' || $rawParam === 'help') {
tgApi::sendMessage(array( $this->sendHelp(); // 显示使用说明
'parse_mode' => 'Markdown', } else if (!(new Domain)->isHost($rawParam)) {
'text' => '*Usage:* `/ntp IP/Domain`',
'reply_markup' => json_encode(array( // 获取NTP服务列表
'inline_keyboard' => array([[
'text' => 'Get NTP Servers',
'callback_data' => '/ntp servers'
]])
))
));
return;
}
if (!$this->isHost($rawParam)) {
tgApi::sendText('Illegal host'); // 输入错误 tgApi::sendText('Illegal host'); // 输入错误
return; } else {
$this->sendNtpStatus($rawParam); // 检查并发送NTP服务器状态
} }
$message = tgApi::sendMarkdown('`' . $rawParam . '`' . PHP_EOL . 'NTP Server Checking...');
$message = json_decode($message, true);
fastcgi_finish_request(); // 断开连接
tgApi::editMessage(array(
'message_id' => $message['result']['message_id'],
) + $this->checkNtp($rawParam)); // 发起查询并返回结果
} }
public function callback($rawParam) { // NTP测试回调入口 public function callback($rawParam) { // NTP测试回调入口
global $tgEnv;
if ($rawParam === 'servers') { if ($rawParam === 'servers') {
$content = $this->showList(); // 显示可选组 $content = $this->showNtpList(); // 显示可选组
} else { } else {
$content = $this->showNtpServer($rawParam); // 显示指定组的服务器列表 $content = $this->showNtpServer($rawParam); // 显示指定组的服务器列表
} }
tgApi::editMessage(array( tgApi::editMessage(array(
'message_id' => $tgEnv['messageId'] 'message_id' => $GLOBALS['tgEnv']['messageId']
) + $content); // 输出结果 ) + $content); // 输出结果
} }
} }

4
models/punycode.php

@ -1,6 +1,6 @@
<?php <?php
class punycodeEntry { class punycodeEntry { // Punycode编码转换入口
private function checkErr($punycode) { // 处理Punycode库错误警告 private function checkErr($punycode) { // 处理Punycode库错误警告
if ($punycode->errFlag) { if ($punycode->errFlag) {
return array( return array(
@ -41,7 +41,7 @@ class punycodeEntry {
} }
} }
if (isset($errMsg)) { // 存在警告 if (isset($errMsg)) { // 存在警告
if (substr($rawParam, 0, 1) !== '.' && substr($rawParam, -1) !== '.') { // 首尾不为. if (substr($rawParam, 0, 1) !== '.' && substr($rawParam, -1) !== '.') { // 首尾不为.时发送警告内容
$msg .= '*Warning:* ' . $errMsg; $msg .= '*Warning:* ' . $errMsg;
} }
} }

191
models/tgDC.php

@ -1,7 +1,7 @@
<?php <?php
class tgDC { class tgDC { // 查询用户DC
private function getDcDetail($dc) { // 获取DC信息 private function getDcDetail($dc) { // 返回DC详细信息
switch ($dc) { switch ($dc) {
case 'DC1': case 'DC1':
return array( return array(
@ -24,153 +24,130 @@ class tgDC {
'addr' => '新加坡' 'addr' => '新加坡'
); );
default: default:
return array(); return array(); // 错误输入
} }
} }
private function curl($url, $timeOut = 5) { // curl模拟 默认5s超时
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $timeOut);
$content = curl_exec($curl);
curl_close($curl);
return $content;
}
private function checkAccount($account) { // 检查用户名是否合法
preg_match('/^[a-zA-Z0-9_]+$/', $account, $match);
if (count($match) === 0 or strlen($account) < 5) { // 用户名由至少5位 0-9/a-z/A-Z/_ 组成
return false;
}
if (substr($account, 0, 1) === '_' || substr($account, -1) === '_') { // 不能以_开头结尾
return false;
}
return true;
}
private function getUserInfo($account) { // 获取Telegram用户信息 private function getUserInfo($account) { // 获取Telegram用户信息
$info['account'] = $account;
$info['name'] = null;
$info['dc'] = null; $info['dc'] = null;
$info['name'] = null;
$html = $this->curl('https://t.me/' . $account); // 获取原始HTML数据 $info['account'] = $account;
$html = (new Curl)->get('https://t.me/' . $account); // 获取原始HTML数据
$html = preg_replace('/[\t\n\r]+/', '', $html); // 去除干扰 $html = preg_replace('/[\t\n\r]+/', '', $html); // 去除干扰
if (!is_string($html) || $html == '') { return $info; } // 用户名无效 if (!is_string($html) || $html == '') {
return $info + [ 'time' => time() ]; // 用户名无效
}
$avatarRegex = '/<img class="tgme_page_photo_image" src="([^<>]+)">/'; $avatarRegex = '/<img class="tgme_page_photo_image" src="([^<>]+)">/';
$nameRegex = '/<span dir="auto">(.+?)<\/span>/'; $nameRegex = '/<span dir="auto">(.+?)<\/span>/';
preg_match($avatarRegex, $html, $avatarMatch); // 匹配目标头像 preg_match($avatarRegex, $html, $avatarMatch); // 匹配目标头像
preg_match($nameRegex, $html, $nameMatch); // 匹配目标名称 preg_match($nameRegex, $html, $nameMatch); // 匹配目标名称
if ($nameMatch[1]) { if (isset($nameMatch[1])) {
$info['name'] = $nameMatch[1]; // 获取用户名 $info['name'] = $nameMatch[1]; // 获取用户名
} }
if ($avatarMatch[1]) { if (isset($avatarMatch[1])) { // 头像可见
$avatarUrl = $avatarMatch[1]; // 获取头像链接 $dcRegex = '/https:\/\/cdn([1-5])\.telesco\.pe\//';
} preg_match($dcRegex, $avatarMatch[1], $dcMatch); // 根据cdn?.telesco.pe获取DC
if ($avatarUrl) { // 头像存在 if (isset($dcMatch[1])) {
$dcRegex = '/https:\/\/cdn(.+)\.telesco\.pe\//'; $info['dc'] = 'DC' . $dcMatch[1]; // DC匹配成功
preg_match_all($dcRegex, $avatarUrl, $dcMatch); // 根据cdn?.telesco.pe获取DC
if ($dcMatch[1]) {
$info['dc'] = 'DC' . $dcMatch[1][0];
} }
} }
if ($info['dc']) { if ($info['dc'] != null) {
$info += $this->getDcDetail($info['dc']); // 匹配DC参数 $info += $this->getDcDetail($info['dc']); // 载入DC详细信息
} }
$info['time'] = time(); // 记录查询时间戳
return $info; return $info;
} }
private function getUserInfoCache($account) { // 获取用户信息 带缓存 public function getInfo($account, $isCache = true) { // 获取用户信息 默认带缓存
$redis = new redisCache('tgdc'); $redis = new RedisCache('tgdc');
$info = $redis->getData($account); // 查询缓存数据 $info = $redis->getData($account); // 查询缓存数据
if (!$info) { // 缓存未命中 if (!$isCache || !$info) { // 不缓存 或 缓存未命中
$info = $this->getUserInfo($account); // 发起查询 $info = $this->getUserInfo($account); // 发起查询
if (!$info['name'] && !$info['dc']) { // 用户名与头像均无 $redis->setData($account, json_encode($info)); // 缓存数据 永久
$cacheTTL = 300; // 缓存5min
} else if ($info['name'] && !$info['dc']) { // 存在用户名但未设置头像
$cacheTTL = 20; // 缓存20s
} else {
$cacheTTL = 3600; // 其余情况缓存1h
}
$redis->setData($account, json_encode($info), $cacheTTL); // 缓存数据
} else { // 缓存命中 } else { // 缓存命中
$info = json_decode($info, true); // 使用缓存数据 $info = json_decode($info, true); // 使用缓存数据
} }
return $info; return $info;
} }
}
public function getInfo($account) { // 查询入口 class tgDCEntry { // DC查询入口
if (substr($account, 0, 1) === '@') { // 用户名可带有@ private function checkAccount($account) { // 检查用户合法性
$account = substr($account, 1); preg_match('/^[a-zA-Z0-9_]+$/', $account, $match);
if (count($match) === 0 or strlen($account) < 5) { // 用户名由至少5位 0-9/a-z/A-Z/_ 组成
return false;
} }
if (!$this->checkAccount($account)) { // 用户名不合法 if (substr($account, 0, 1) === '_' || substr($account, -1) === '_') { // 不能以_开头结尾
return array( return false;
'status' => 'error',
'message' => '用户名无效'
);
} }
$info = $this->getUserInfoCache($account); return true;
}
private function sendHelp() { // 显示帮助信息
$message = tgApi::sendMarkdown('*Usage:* `/dc username`');
$message = json_decode($message, true);
return $message['result']['message_id']; // 返回消息ID
}
private function genMessage($info) { // 生成返回信息
if (!$info['name'] && !$info['dc']) { // 用户名与头像均无 if (!$info['name'] && !$info['dc']) { // 用户名与头像均无
return array( return '@' . $info['account'] . ' 无法识别';
'status' => 'error',
'message' => '@' . $account . ' 无法识别'
);
} else if ($info['name'] && !$info['dc']) { // 存在用户名但未设置头像 } else if ($info['name'] && !$info['dc']) { // 存在用户名但未设置头像
return array( return '@' . $info['account'] . ' 未设置头像或不可见';
'status' => 'error',
'message' => '@' . $account . ' 未设置头像或不可见'
);
} }
return array( $msg = '@' . $info['account'] . ' (' . $info['name'] . ')' . PHP_EOL;
'status' => 'ok', $msg .= '_' . $info['as'] . '_ ';
'data' => json_encode($info) // 返回查询结果 $msg .= '`(``' . $info['ip'] . '``)`' . PHP_EOL;
); $msg .= '*' . $info['dc'] . '* - ' . $info['addr'] . PHP_EOL;
return $msg; // 返回正常查询结果
} }
}
class tgDCEntry { private function sendInfo($account) { // 查询并发送用户信息
private function getInfo($account) { if (!$this->checkAccount($account)) { // 用户名不合法
$content = (new tgDC)->getInfo($account); // 发起查询 tgApi::sendText('用户名无效');
if ($content['status'] === 'ok') { return;
$info = json_decode($content['data'], true); }
$msg = '@' . $info['account'] . ' (' . $info['name'] . ')' . PHP_EOL; $info = (new tgDC)->getInfo($account); // 带缓存查询
$msg .= '<i>' . $info['as'] . '</i> '; $message = tgApi::sendMarkdown($this->genMessage($info)); // 发送预查询信息
$msg .= '<code>(</code><code>' . $info['ip'] . '</code><code>)</code>' . PHP_EOL; if (!$info['name'] && !$info['dc']) {
$msg .= '<b>' . $info['dc'] . '</b> - ' . $info['addr'] . PHP_EOL; $cacheTime = 300; // 未设置用户名或用户不存在 缓存5min
return array( } else if ($info['name'] && !$info['dc']) {
'parse_mode' => 'HTML', // HTML格式输出 $cacheTime = 20; // 用户头像不可见 缓存20s
'text' => $msg
);
} else { } else {
return array( $cacheTime = 86400; // 用户正常 缓存24h
'text' => $content['message'] // 返回错误信息 }
); if ($cacheTime < time() - $info['time']) { // 数据过期
$messageId = json_decode($message, true)['result']['message_id'];
$infoRenew = (new tgDC)->getInfo($account, false); // 不带缓存 重新查询
unset($info['time']);
unset($infoRenew['time']);
if ($info !== $infoRenew) { // 数据出现变化
tgApi::editMessage(array(
'parse_mode' => 'Markdown',
'message_id' => $messageId,
'text' => $this->genMessage($infoRenew) // 更新信息
));
}
} }
} }
public function query($rawParam) { // tgDC查询入口 public function query($rawParam) { // tgDC查询入口
$helpMsg = array( // 使用说明
'parse_mode' => 'Markdown',
'text' => '*Usage:* `/dc username`'
);
if ($rawParam === 'help') { // 查询使用说明
tgApi::sendMessage($helpMsg);
return;
}
if ($rawParam !== '') { // 查询指定用户数据
tgApi::sendMessage($this->getInfo($rawParam));
return;
}
global $tgEnv; global $tgEnv;
if (!$tgEnv['isGroup']) { // 群组不发送帮助信息 if ($rawParam === 'help') { $this->sendHelp(); } // 显示使用说明
$message = json_decode(tgApi::sendMessage($helpMsg), true); // 发送使用说明 if ($rawParam == '') {
$rawParam = $tgEnv['userAccount']; // 空指令时查询对方信息
if (!$tgEnv['isGroup']) {
$messageId = $this->sendHelp(); // 非群组发送使用说明
}
}
if (substr($rawParam, 0, 1) === '@') {
$rawParam = substr($rawParam, 1); // 去除用户名前@
} }
tgApi::sendMessage($this->getInfo($tgEnv['userAccount'])); // 查询对方用户名 $this->sendInfo($rawParam); // 查询并发送用户信息
if ($tgEnv['isGroup']) { return; } if (!isset($messageId)) { return; }
fastcgi_finish_request(); // 断开连接
sleep(10); // 延迟10s sleep(10); // 延迟10s
tgApi::deleteMessage(array( // 删除使用说明 tgApi::deleteMessage(array( // 删除使用说明
'message_id' => $message['result']['message_id'] 'message_id' => $messageId
)); ));
} }
} }

113
models/tldQuery.php

@ -1,14 +1,14 @@
<?php <?php
class tldQueryEntry { class tldQueryEntry { // TLD信息查询入口
private $tldDB = './db/tldInfo.db'; private $tldDB = './db/tldInfo.db'; // TLD信息数据库
private function getTldInfo($tld) { private function getTldInfo($tld) { // 获取TLD详细信息
$db = new SqliteDB($this->tldDB); $db = new SqliteDB($this->tldDB);
$res = $db->query('SELECT * FROM `iana` WHERE tld="' . $tld . '";'); $res = $db->query('SELECT * FROM `iana` WHERE tld="' . $tld . '";');
$info = $res->fetchArray(SQLITE3_ASSOC); $info = $res->fetchArray(SQLITE3_ASSOC);
if (!$info) { return null; } if (!$info) { return null; } // 查无该TLD
$info['manager'] = json_decode(base64_decode($info['manager']), true); $info['manager'] = json_decode(base64_decode($info['manager']), true); // Base64解码 + JSON解码
$info['admin_contact'] = json_decode(base64_decode($info['admin_contact']), true); $info['admin_contact'] = json_decode(base64_decode($info['admin_contact']), true);
$info['tech_contact'] = json_decode(base64_decode($info['tech_contact']), true); $info['tech_contact'] = json_decode(base64_decode($info['tech_contact']), true);
$info['nameserver'] = json_decode(base64_decode($info['nameserver']), true); $info['nameserver'] = json_decode(base64_decode($info['nameserver']), true);
@ -16,14 +16,14 @@ class tldQueryEntry {
return $info; return $info;
} }
private function genMessage($info) { // 生成返回消息 private function genMessage($info, $icp, $subTld) { // 生成返回消息
$msg = '`' . $info['tld'] . '` `(` `' . $info['type']; $msg = '`' . (new Punycode)->decode($info['tld']) . '` `(` `' . $info['type'];
if ($info['active'] === 'yes') { if ($info['active'] === 'yes') { // TLD是否有效解析
$msg .= '` `)`' . PHP_EOL; $msg .= '` `)`' . PHP_EOL;
} else { } else {
$msg .= ' not active` `)`' . PHP_EOL; $msg .= ' not active` `)`' . PHP_EOL;
} }
if (count($info['manager']) !== 0) { if (count($info['manager']) !== 0) { // 域名管理者信息
$msg .= '*Manager*' . PHP_EOL; $msg .= '*Manager*' . PHP_EOL;
foreach ($info['manager']['name'] as $row) { foreach ($info['manager']['name'] as $row) {
$msg .= ' ' . $row . PHP_EOL; $msg .= ' ' . $row . PHP_EOL;
@ -32,7 +32,7 @@ class tldQueryEntry {
$msg .= ' _' . $row . '_' . PHP_EOL; $msg .= ' _' . $row . '_' . PHP_EOL;
} }
} }
if (count($info['admin_contact']) !== 0) { if (count($info['admin_contact']) !== 0) { // 域名管理员联系人
$contact = $info['admin_contact']; $contact = $info['admin_contact'];
$msg .= '*Administrative Contact*' . PHP_EOL; $msg .= '*Administrative Contact*' . PHP_EOL;
$msg .= ' ' . $contact['name'] . PHP_EOL; $msg .= ' ' . $contact['name'] . PHP_EOL;
@ -50,7 +50,7 @@ class tldQueryEntry {
$msg .= ' Fax: _' . $contact['fax'] . '_' . PHP_EOL; $msg .= ' Fax: _' . $contact['fax'] . '_' . PHP_EOL;
} }
} }
if (count($info['tech_contact']) !== 0) { if (count($info['tech_contact']) !== 0) { // 域名技术支持联系人
$contact = $info['tech_contact']; $contact = $info['tech_contact'];
$msg .= '*Technical Contact*' . PHP_EOL; $msg .= '*Technical Contact*' . PHP_EOL;
$msg .= ' ' . $contact['name'] . PHP_EOL; $msg .= ' ' . $contact['name'] . PHP_EOL;
@ -68,7 +68,7 @@ class tldQueryEntry {
$msg .= ' Fax: _' . $contact['fax'] . '_' . PHP_EOL; $msg .= ' Fax: _' . $contact['fax'] . '_' . PHP_EOL;
} }
} }
if (count($info['nameserver']) !== 0) { if (count($info['nameserver']) !== 0) { // 域名NS服务器信息
$nameserver = $info['nameserver']; $nameserver = $info['nameserver'];
$msg .= '*Name Servers*' . PHP_EOL; $msg .= '*Name Servers*' . PHP_EOL;
foreach ($nameserver as $host => $ips) { foreach ($nameserver as $host => $ips) {
@ -78,52 +78,28 @@ class tldQueryEntry {
} }
} }
} }
if (count($info['dnssec']) !== 0) { if ($subTld !== null) { // 输出次级顶级域
$msg .= '*Sub TLD*' . PHP_EOL;
foreach ($subTld as $tld) {
$msg .= ' `' . (new Punycode)->decode($tld) . '`' . PHP_EOL;
}
}
if (count($info['dnssec']) !== 0) { // 域名DNSSEC状态
if ($info['dnssec']['type'] === 1) { // 正常DNSSEC if ($info['dnssec']['type'] === 1) { // 正常DNSSEC
$msg .= '*DNSSEC*' . PHP_EOL; $msg .= '*DNSSEC*' . PHP_EOL;
foreach ($info['dnssec']['ds'] as $ds) { foreach ($info['dnssec']['ds'] as $ds) {
$msg .= ' *Tag: ' . $ds['tag'] . '*' . PHP_EOL; $msg .= ' *Tag: ' . $ds['tag'] . '*' . PHP_EOL;
if (strlen($ds['hash']) === 64) { if (strlen($ds['hash']) === 64) { // SHA256
$msg .= ' `' . substr($ds['hash'], 0, 32) . '`' . PHP_EOL; $msg .= ' `' . substr($ds['hash'], 0, 32) . '`' . PHP_EOL;
$msg .= ' `' . substr($ds['hash'], -32) . '`' . PHP_EOL; $msg .= ' `' . substr($ds['hash'], -32) . '`' . PHP_EOL;
} else if (strlen($ds['hash']) === 40){ } else if (strlen($ds['hash']) === 40){ // SHA1
$msg .= ' `' . substr($ds['hash'], 0, 32) . '`' . PHP_EOL; $msg .= ' `' . substr($ds['hash'], 0, 32) . '`' . PHP_EOL;
$msg .= ' `' . substr($ds['hash'], -8) . '`' . PHP_EOL; $msg .= ' `' . substr($ds['hash'], -8) . '`' . PHP_EOL;
} }
$msg .= ' Algorithm: _' . $ds['algorithm']; $msg .= ' Algorithm: _' . $ds['algorithm'] . ' ('; // 算法类型
if ($ds['algorithm'] === '1') { $msg .= (new DNSSEC)->algorithmDesc($ds['algorithm']) . ')_' . PHP_EOL;
$msg .= ' (RSA/MD5)'; $msg .= ' Digest type: _' . $ds['digest'] . ' ('; // 摘要类型
} else if ($ds['algorithm'] === '3') { $msg .= (new DNSSEC)->digestDesc($ds['digest']) . ')_' . PHP_EOL;
$msg .= ' (DSA/SHA1)';
} else if ($ds['algorithm'] === '5') {
$msg .= ' (RSA/SHA-1)';
} else if ($ds['algorithm'] === '6') {
$msg .= ' (DSA-NSEC3-SHA1)';
} else if ($ds['algorithm'] === '7') {
$msg .= ' (RSASHA1-NSEC3-SHA1)';
} else if ($ds['algorithm'] === '8') {
$msg .= ' (RSA/SHA-256)';
} else if ($ds['algorithm'] === '10') {
$msg .= ' (RSA/SHA-512)';
} else if ($ds['algorithm'] === '12') {
$msg .= ' (GOST R 34.10-2001)';
} else if ($ds['algorithm'] === '13') {
$msg .= ' (ECDSA Curve P-256 with SHA-256)';
} else if ($ds['algorithm'] === '14') {
$msg .= ' (ECDSA Curve P-384 with SHA-384)';
} else if ($ds['algorithm'] === '15') {
$msg .= ' (Ed25519)';
} else if ($ds['algorithm'] === '16') {
$msg .= ' (Ed448)';
}
$msg .= '_' . PHP_EOL;
$msg .= ' Digest type: _' . $ds['digest'];
if ($ds['digest'] === '1') {
$msg .= ' (SHA-1)';
} else if ($ds['digest'] === '2') {
$msg .= ' (SHA-256)';
}
$msg .= '_' . PHP_EOL;
} }
} else if ($info['dnssec']['type'] === 3) { // 启用DNSSEC 但未部署DS记录 } else if ($info['dnssec']['type'] === 3) { // 启用DNSSEC 但未部署DS记录
$msg .= '*DNSSEC:* signed, but without DS record.' . PHP_EOL; $msg .= '*DNSSEC:* signed, but without DS record.' . PHP_EOL;
@ -131,28 +107,53 @@ class tldQueryEntry {
$msg .= '*DNSSEC:* unsigned' . PHP_EOL; $msg .= '*DNSSEC:* unsigned' . PHP_EOL;
} }
} }
if ($info['website'] != '') { if ($icp !== null) { // ICP备案信息
$msg .= '*ICP Detail*' . PHP_EOL;
$msg .= ' 管理机构: ';
if ($icp['org'] === '空') {
$msg .= '未知' . PHP_EOL;
} else {
$msg .= $icp['org'] . PHP_EOL;
$msg .= ' 机构主页: ';
foreach ($icp['site'] as $site) {
$msg .= '`' . $site . '` ';
}
$msg .= PHP_EOL;
}
}
if ($info['website'] != '') { // 所有者主页
$msg .= '*Website:* ' . $info['website'] . PHP_EOL; $msg .= '*Website:* ' . $info['website'] . PHP_EOL;
} }
if ($info['whois'] != '') { if ($info['whois'] != '') { // Whois服务器信息
$msg .= '*Whois Server:* `' . $info['whois'] . '`' . PHP_EOL; $msg .= '*Whois Server:* `' . $info['whois'] . '`' . PHP_EOL;
} }
$msg .= '*Registration date:* _' . $info['regist_date'] . '_' . PHP_EOL; $msg .= '*Registration date:* _' . $info['regist_date'] . '_' . PHP_EOL; // 注册日期
$msg .= '*Record last updated:* _' . $info['last_updated'] . '_' . PHP_EOL; $msg .= '*Record last updated:* _' . $info['last_updated'] . '_' . PHP_EOL; // 最后更改时间
return $msg; return $msg;
} }
public function query($rawParam) { // TLD数据查询入口 public function query($rawParam) { // TLD数据查询入口
if ($rawParam == '' || $rawParam === 'help') {
tgApi::sendMessage(array( // 发送使用说明
'parse_mode' => 'Markdown',
'text' => '*Usage:* `/tld top-level-domain`',
));
return;
}
if (substr($rawParam, 0, 1) !== '.') { // 补上. if (substr($rawParam, 0, 1) !== '.') { // 补上.
$rawParam = '.' . $rawParam; $rawParam = '.' . $rawParam;
} }
$rawParam = (new Punycode)->encode($rawParam);
$info = $this->getTldInfo($rawParam); $info = $this->getTldInfo($rawParam);
if (!$info) { if (!$info) { // 查无该TLD
tgApi::sendMarkdown('`' . $rawParam . '`' . PHP_EOL . 'TLD not found'); $rawParam = (new Punycode)->decode($rawParam);
tgApi::sendMarkdown('`' . $rawParam . '`' . ' not found');
return; return;
} }
$icp = (new Domain)->icpTldInfo($rawParam); // 查询ICP信息
$subTld = (new Domain)->getSubTld($rawParam); // 查询次级域信息
tgApi::sendMessage(array( tgApi::sendMessage(array(
'text' => $this->genMessage($info), 'text' => $this->genMessage($info, $icp, $subTld),
'parse_mode' => 'Markdown', // Markdown格式输出 'parse_mode' => 'Markdown', // Markdown格式输出
'disable_web_page_preview' => 'true' // 不显示页面预览 'disable_web_page_preview' => 'true' // 不显示页面预览
)); ));

3
route.php

@ -32,7 +32,7 @@ function cmdRoute($cmd) { // 命令功能模块路由
case '/punycode': case '/punycode':
return (new punycodeEntry); return (new punycodeEntry);
} }
return null; return null; // 命令不存在
} }
function route($message) { // 请求路由 function route($message) { // 请求路由
@ -52,6 +52,7 @@ function route($message) { // 请求路由
$cmd = substr($cmd, 0, strlen($cmd) - strlen($botAccount) - 1); // 分离@机器人 $cmd = substr($cmd, 0, strlen($cmd) - strlen($botAccount) - 1); // 分离@机器人
} }
} }
$rawParam = trim($rawParam);
$entry = cmdRoute($cmd); // 获取功能模块入口 $entry = cmdRoute($cmd); // 获取功能模块入口
if (!$entry) { return; } // 命令不存在 if (!$entry) { return; } // 命令不存在
if ($tgEnv['isCallback']) { if ($tgEnv['isCallback']) {

Loading…
Cancel
Save