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_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

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
class extractDomain {
class Domain { // 域名相关功能
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);
$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;
}
}
?>

10
functions/RedisCache.php

@ -1,6 +1,6 @@
<?php
class redisCache {
class RedisCache {
private $redisSetting = array(); // Redis接口参数
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->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;
}
}

4
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(); // 关闭数据库连接
}
}

10
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, // 是否为回调请求

123
models/cfopPic.php

@ -1,91 +1,74 @@
<?php
class cfopPicEntry {
private function getCfopMsg() {
return array(
class cfopPicEntry { // 获取CFOP公式图片
private function sendMenu() { // 发送菜单
$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魔方公式合集',
'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']
));
}
}

208
models/icpQuery.php

@ -1,161 +1,56 @@
<?php
class icpQuery {
class icpQuery { // ICP备案查询
private $apiPath;
public function __construct() {
global $env;
$this->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']); // 查询并输出域名备案信息
}
}
}

176
models/ipInfo.php

@ -1,11 +1,10 @@
<?php
class ipInfo {
class ipInfo { // 查询IP详细信息
private $apiPath;
public function __construct() {
global $env;
$this->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 = '<b>IP:</b> <code>' . $info['ip'] . '</code>' . PHP_EOL;
if ($info['as'] != NULL) {
$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['detail'] != NULL) { $msg .= '<b>Detail:</b> ' . $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); // 逐个输出
}

354
models/kmsCheck.php

@ -1,16 +1,16 @@
<?php
class kmsKeys {
private $kmsDB = './db/kmsKeys.db'; // KMS密钥数据库
private function getVersionName($type, $version_id) { // 获取对应版本的名称
$db = new SqliteDB($this->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);
}
}

213
models/ntpCheck.php

@ -1,28 +1,27 @@
<?php
class ntpList {
private $ntpDB = './db/ntpServer.db'; // NTP服务器数据库
class ntpServer { // 获取NTP服务器列表
private $db, $ntpDB = './db/ntpServer.db'; // NTP服务器数据库
private function getListName($list_id) { // 获取对应组的名称
$db = new SqliteDB($this->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]+<footer>/', $html, $match); // 切取数据部分
preg_match('/<label>[\s\S]+<footer>/', $match[0], $match); // 去除前部干扰
$match = substr($match[0], 0, strlen($match[0]) - 8); // 去除后部<footer>
@ -83,15 +120,23 @@ class ntpCheck {
$group['Offset'] = $this->formatOffset($group['Offset']); // 转换为ms
$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服务器状态 带缓存
$redis = new redisCache('ntp');
public function ntpStatus($host) { // 检测NTP服务器状态
$redis = new RedisCache('ntp');
$servers = $redis->getData($host); // 查询缓存数据
if (!$servers) { // 缓存未命中
$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 { // 缓存命中
$servers = json_decode($servers, true); // 使用缓存数据
}
@ -99,24 +144,13 @@ class ntpCheck {
}
}
class ntpCheckEntry {
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;
}
class ntpCheckEntry { // NTP功能入口
private function hashGroupName($str) { // 计算组名的哈希值 取前12位
return substr(md5($str), 0, 12);
}
private function showList() { // 列出所有NTP服务器组
$ntpList = (new ntpList)->getNtpList();
private function showNtpList() { // 列出所有NTP服务器组
$ntpList = (new ntpServer)->getList();
foreach ($ntpList as $index => $ntpHosts) {
$buttons[] = array([ // 生成按钮列表
'text' => $index,
@ -125,29 +159,29 @@ class ntpCheckEntry {
}
return array(
'text' => 'Which one did you like?',
'reply_markup' => json_encode(array( // 列表按钮
'reply_markup' => json_encode(array( // 按钮列表
'inline_keyboard' => $buttons
))
);
}
private function showNtpServer($targetGroup) { // 列出指定组的NTP服务器
$ntpList = (new ntpList)->getNtpList();
foreach ($ntpList as $index => $ntpHosts) {
$ntpList = (new ntpServer)->getList();
foreach ($ntpList as $index => $ntpHosts) { // 搜索目标组
if ($this->hashGroupName($index) === $targetGroup) { break; }
}
$msg = '*' . $index . '*' . PHP_EOL;
$msg = '*' . $index . '*' . PHP_EOL; // 显示组名
foreach ($ntpHosts as $ntpHost) {
if ($ntpHost['desc'] !== '') {
$msg .= $ntpHost['desc'] . ':';
$msg .= $ntpHost['desc'] . ':'; // 服务器描述
}
$msg .= '`' . $ntpHost['host'] . '`' . PHP_EOL;
$msg .= '`' . $ntpHost['host'] . '`' . PHP_EOL; // 服务器地址
}
return array(
'parse_mode' => 'Markdown',
'text' => $msg,
'parse_mode' => 'Markdown',
'reply_markup' => json_encode(array(
'inline_keyboard' => array([[
'inline_keyboard' => array([[ // 返回上一级 按钮
'text' => ' << Go back <<',
'callback_data' => '/ntp servers'
]])
@ -155,65 +189,76 @@ class ntpCheckEntry {
);
}
private function checkNtp($host) { // 检查NTP服务器状态
$servers = (new ntpCheck)->ntpStatus($host);
if (count($servers) === 0) {
private function genMessage($host, $servers) { // 生成返回信息
if (count($servers) === 0) { // NTP服务不可用
$msg = '`' . $host . '`' . PHP_EOL;
$msg .= 'NTP Server *Offline*' . PHP_EOL . PHP_EOL;
return array(
'parse_mode' => 'Markdown',
'text' => $msg
);
return $msg;
}
$msg = '`' . $host . '`' . PHP_EOL;
$msg .= 'NTP Server *Normally*' . PHP_EOL . PHP_EOL;
foreach ($servers as $server) {
$msg .= 'NTP Server *Normally*' . PHP_EOL . PHP_EOL; // NTP服务器正常
foreach ($servers as $server) { // 显示所有服务器
$msg .= '`' . $server['Server'] . '`' . PHP_EOL;
$msg .= '_Stratum:_ ' . $server['Stratum'] . PHP_EOL;
$msg .= '_Offset:_ ' . $server['Offset'] . 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',
'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测试查询入口
if ($rawParam == '' || $rawParam === 'help') { // 显示使用说明
tgApi::sendMessage(array(
'parse_mode' => 'Markdown',
'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)) {
if ($rawParam == '' || $rawParam === 'help') {
$this->sendHelp(); // 显示使用说明
} else if (!(new Domain)->isHost($rawParam)) {
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测试回调入口
global $tgEnv;
if ($rawParam === 'servers') {
$content = $this->showList(); // 显示可选组
$content = $this->showNtpList(); // 显示可选组
} else {
$content = $this->showNtpServer($rawParam); // 显示指定组的服务器列表
}
tgApi::editMessage(array(
'message_id' => $tgEnv['messageId']
'message_id' => $GLOBALS['tgEnv']['messageId']
) + $content); // 输出结果
}
}

4
models/punycode.php

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

191
models/tgDC.php

@ -1,7 +1,7 @@
<?php
class tgDC {
private function getDcDetail($dc) { // 获取DC信息
class tgDC { // 查询用户DC
private function getDcDetail($dc) { // 返回DC详细信息
switch ($dc) {
case 'DC1':
return array(
@ -24,153 +24,130 @@ class tgDC {
'addr' => '新加坡'
);
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用户信息
$info['account'] = $account;
$info['name'] = null;
$info['dc'] = null;
$html = $this->curl('https://t.me/' . $account); // 获取原始HTML数据
$info['name'] = null;
$info['account'] = $account;
$html = (new Curl)->get('https://t.me/' . $account); // 获取原始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="([^<>]+)">/';
$nameRegex = '/<span dir="auto">(.+?)<\/span>/';
preg_match($avatarRegex, $html, $avatarMatch); // 匹配目标头像
preg_match($nameRegex, $html, $nameMatch); // 匹配目标名称
if ($nameMatch[1]) {
if (isset($nameMatch[1])) {
$info['name'] = $nameMatch[1]; // 获取用户名
}
if ($avatarMatch[1]) {
$avatarUrl = $avatarMatch[1]; // 获取头像链接
}
if ($avatarUrl) { // 头像存在
$dcRegex = '/https:\/\/cdn(.+)\.telesco\.pe\//';
preg_match_all($dcRegex, $avatarUrl, $dcMatch); // 根据cdn?.telesco.pe获取DC
if ($dcMatch[1]) {
$info['dc'] = 'DC' . $dcMatch[1][0];
if (isset($avatarMatch[1])) { // 头像可见
$dcRegex = '/https:\/\/cdn([1-5])\.telesco\.pe\//';
preg_match($dcRegex, $avatarMatch[1], $dcMatch); // 根据cdn?.telesco.pe获取DC
if (isset($dcMatch[1])) {
$info['dc'] = 'DC' . $dcMatch[1]; // DC匹配成功
}
}
if ($info['dc']) {
$info += $this->getDcDetail($info['dc']); // 匹配DC参数
if ($info['dc'] != null) {
$info += $this->getDcDetail($info['dc']); // 载入DC详细信息
}
$info['time'] = time(); // 记录查询时间戳
return $info;
}
private function getUserInfoCache($account) { // 获取用户信息 带缓存
$redis = new redisCache('tgdc');
public function getInfo($account, $isCache = true) { // 获取用户信息 默认带缓存
$redis = new RedisCache('tgdc');
$info = $redis->getData($account); // 查询缓存数据
if (!$info) { // 缓存未命中
if (!$isCache || !$info) { // 不缓存 或 缓存未命中
$info = $this->getUserInfo($account); // 发起查询
if (!$info['name'] && !$info['dc']) { // 用户名与头像均无
$cacheTTL = 300; // 缓存5min
} else if ($info['name'] && !$info['dc']) { // 存在用户名但未设置头像
$cacheTTL = 20; // 缓存20s
} else {
$cacheTTL = 3600; // 其余情况缓存1h
}
$redis->setData($account, json_encode($info), $cacheTTL); // 缓存数据
$redis->setData($account, json_encode($info)); // 缓存数据 永久
} else { // 缓存命中
$info = json_decode($info, true); // 使用缓存数据
}
return $info;
}
}
public function getInfo($account) { // 查询入口
if (substr($account, 0, 1) === '@') { // 用户名可带有@
$account = substr($account, 1);
class tgDCEntry { // DC查询入口
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 (!$this->checkAccount($account)) { // 用户名不合法
return array(
'status' => 'error',
'message' => '用户名无效'
);
if (substr($account, 0, 1) === '_' || substr($account, -1) === '_') { // 不能以_开头结尾
return false;
}
$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']) { // 用户名与头像均无
return array(
'status' => 'error',
'message' => '@' . $account . ' 无法识别'
);
return '@' . $info['account'] . ' 无法识别';
} else if ($info['name'] && !$info['dc']) { // 存在用户名但未设置头像
return array(
'status' => 'error',
'message' => '@' . $account . ' 未设置头像或不可见'
);
return '@' . $info['account'] . ' 未设置头像或不可见';
}
return array(
'status' => 'ok',
'data' => json_encode($info) // 返回查询结果
);
$msg = '@' . $info['account'] . ' (' . $info['name'] . ')' . PHP_EOL;
$msg .= '_' . $info['as'] . '_ ';
$msg .= '`(``' . $info['ip'] . '``)`' . PHP_EOL;
$msg .= '*' . $info['dc'] . '* - ' . $info['addr'] . PHP_EOL;
return $msg; // 返回正常查询结果
}
}
class tgDCEntry {
private function getInfo($account) {
$content = (new tgDC)->getInfo($account); // 发起查询
if ($content['status'] === 'ok') {
$info = json_decode($content['data'], true);
$msg = '@' . $info['account'] . ' (' . $info['name'] . ')' . PHP_EOL;
$msg .= '<i>' . $info['as'] . '</i> ';
$msg .= '<code>(</code><code>' . $info['ip'] . '</code><code>)</code>' . PHP_EOL;
$msg .= '<b>' . $info['dc'] . '</b> - ' . $info['addr'] . PHP_EOL;
return array(
'parse_mode' => 'HTML', // HTML格式输出
'text' => $msg
);
private function sendInfo($account) { // 查询并发送用户信息
if (!$this->checkAccount($account)) { // 用户名不合法
tgApi::sendText('用户名无效');
return;
}
$info = (new tgDC)->getInfo($account); // 带缓存查询
$message = tgApi::sendMarkdown($this->genMessage($info)); // 发送预查询信息
if (!$info['name'] && !$info['dc']) {
$cacheTime = 300; // 未设置用户名或用户不存在 缓存5min
} else if ($info['name'] && !$info['dc']) {
$cacheTime = 20; // 用户头像不可见 缓存20s
} else {
return array(
'text' => $content['message'] // 返回错误信息
);
$cacheTime = 86400; // 用户正常 缓存24h
}
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查询入口
$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;
if (!$tgEnv['isGroup']) { // 群组不发送帮助信息
$message = json_decode(tgApi::sendMessage($helpMsg), true); // 发送使用说明
if ($rawParam === 'help') { $this->sendHelp(); } // 显示使用说明
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'])); // 查询对方用户名
if ($tgEnv['isGroup']) { return; }
fastcgi_finish_request(); // 断开连接
$this->sendInfo($rawParam); // 查询并发送用户信息
if (!isset($messageId)) { return; }
sleep(10); // 延迟10s
tgApi::deleteMessage(array( // 删除使用说明
'message_id' => $message['result']['message_id']
'message_id' => $messageId
));
}
}

113
models/tldQuery.php

@ -1,14 +1,14 @@
<?php
class tldQueryEntry {
private $tldDB = './db/tldInfo.db';
class tldQueryEntry { // TLD信息查询入口
private $tldDB = './db/tldInfo.db'; // TLD信息数据库
private function getTldInfo($tld) {
private function getTldInfo($tld) { // 获取TLD详细信息
$db = new SqliteDB($this->tldDB);
$res = $db->query('SELECT * FROM `iana` WHERE tld="' . $tld . '";');
$info = $res->fetchArray(SQLITE3_ASSOC);
if (!$info) { return null; }
$info['manager'] = json_decode(base64_decode($info['manager']), true);
if (!$info) { return null; } // 查无该TLD
$info['manager'] = json_decode(base64_decode($info['manager']), true); // Base64解码 + JSON解码
$info['admin_contact'] = json_decode(base64_decode($info['admin_contact']), true);
$info['tech_contact'] = json_decode(base64_decode($info['tech_contact']), true);
$info['nameserver'] = json_decode(base64_decode($info['nameserver']), true);
@ -16,14 +16,14 @@ class tldQueryEntry {
return $info;
}
private function genMessage($info) { // 生成返回消息
$msg = '`' . $info['tld'] . '` `(` `' . $info['type'];
if ($info['active'] === 'yes') {
private function genMessage($info, $icp, $subTld) { // 生成返回消息
$msg = '`' . (new Punycode)->decode($info['tld']) . '` `(` `' . $info['type'];
if ($info['active'] === 'yes') { // TLD是否有效解析
$msg .= '` `)`' . PHP_EOL;
} else {
$msg .= ' not active` `)`' . PHP_EOL;
}
if (count($info['manager']) !== 0) {
if (count($info['manager']) !== 0) { // 域名管理者信息
$msg .= '*Manager*' . PHP_EOL;
foreach ($info['manager']['name'] as $row) {
$msg .= ' ' . $row . PHP_EOL;
@ -32,7 +32,7 @@ class tldQueryEntry {
$msg .= ' _' . $row . '_' . PHP_EOL;
}
}
if (count($info['admin_contact']) !== 0) {
if (count($info['admin_contact']) !== 0) { // 域名管理员联系人
$contact = $info['admin_contact'];
$msg .= '*Administrative Contact*' . PHP_EOL;
$msg .= ' ' . $contact['name'] . PHP_EOL;
@ -50,7 +50,7 @@ class tldQueryEntry {
$msg .= ' Fax: _' . $contact['fax'] . '_' . PHP_EOL;
}
}
if (count($info['tech_contact']) !== 0) {
if (count($info['tech_contact']) !== 0) { // 域名技术支持联系人
$contact = $info['tech_contact'];
$msg .= '*Technical Contact*' . PHP_EOL;
$msg .= ' ' . $contact['name'] . PHP_EOL;
@ -68,7 +68,7 @@ class tldQueryEntry {
$msg .= ' Fax: _' . $contact['fax'] . '_' . PHP_EOL;
}
}
if (count($info['nameserver']) !== 0) {
if (count($info['nameserver']) !== 0) { // 域名NS服务器信息
$nameserver = $info['nameserver'];
$msg .= '*Name Servers*' . PHP_EOL;
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
$msg .= '*DNSSEC*' . PHP_EOL;
foreach ($info['dnssec']['ds'] as $ds) {
$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'], -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'], -8) . '`' . PHP_EOL;
}
$msg .= ' Algorithm: _' . $ds['algorithm'];
if ($ds['algorithm'] === '1') {
$msg .= ' (RSA/MD5)';
} else if ($ds['algorithm'] === '3') {
$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;
$msg .= ' Algorithm: _' . $ds['algorithm'] . ' ('; // 算法类型
$msg .= (new DNSSEC)->algorithmDesc($ds['algorithm']) . ')_' . PHP_EOL;
$msg .= ' Digest type: _' . $ds['digest'] . ' ('; // 摘要类型
$msg .= (new DNSSEC)->digestDesc($ds['digest']) . ')_' . PHP_EOL;
}
} else if ($info['dnssec']['type'] === 3) { // 启用DNSSEC 但未部署DS记录
$msg .= '*DNSSEC:* signed, but without DS record.' . PHP_EOL;
@ -131,28 +107,53 @@ class tldQueryEntry {
$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;
}
if ($info['whois'] != '') {
if ($info['whois'] != '') { // Whois服务器信息
$msg .= '*Whois Server:* `' . $info['whois'] . '`' . PHP_EOL;
}
$msg .= '*Registration date:* _' . $info['regist_date'] . '_' . PHP_EOL;
$msg .= '*Record last updated:* _' . $info['last_updated'] . '_' . PHP_EOL;
$msg .= '*Registration date:* _' . $info['regist_date'] . '_' . PHP_EOL; // 注册日期
$msg .= '*Record last updated:* _' . $info['last_updated'] . '_' . PHP_EOL; // 最后更改时间
return $msg;
}
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) !== '.') { // 补上.
$rawParam = '.' . $rawParam;
}
$rawParam = (new Punycode)->encode($rawParam);
$info = $this->getTldInfo($rawParam);
if (!$info) {
tgApi::sendMarkdown('`' . $rawParam . '`' . PHP_EOL . 'TLD not found');
if (!$info) { // 查无该TLD
$rawParam = (new Punycode)->decode($rawParam);
tgApi::sendMarkdown('`' . $rawParam . '`' . ' not found');
return;
}
$icp = (new Domain)->icpTldInfo($rawParam); // 查询ICP信息
$subTld = (new Domain)->getSubTld($rawParam); // 查询次级域信息
tgApi::sendMessage(array(
'text' => $this->genMessage($info),
'text' => $this->genMessage($info, $icp, $subTld),
'parse_mode' => 'Markdown', // Markdown格式输出
'disable_web_page_preview' => 'true' // 不显示页面预览
));

3
route.php

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

Loading…
Cancel
Save