|
|
|
@@ -14,6 +14,7 @@ class Bot
|
|
|
|
|
public $logs;
|
|
|
|
|
public $reg;
|
|
|
|
|
public $pool;
|
|
|
|
|
public $hwid;
|
|
|
|
|
|
|
|
|
|
public function __construct($key, $i18n)
|
|
|
|
|
{
|
|
|
|
@@ -31,6 +32,7 @@ class Bot
|
|
|
|
|
$this->limit = $this->getPacConf()['limitpage'] ?: 5;
|
|
|
|
|
$this->adguard = '/config/AdGuardHome.yaml';
|
|
|
|
|
$this->update = '/update/json';
|
|
|
|
|
$this->hwid = '/config/hwid.json';
|
|
|
|
|
$this->logs = [
|
|
|
|
|
'nginx_default_access',
|
|
|
|
|
'nginx_domain_access',
|
|
|
|
@@ -168,6 +170,30 @@ class Bot
|
|
|
|
|
case preg_match('~^/setIpLimit$~', $this->input['callback'], $m):
|
|
|
|
|
$this->setIpLimit();
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/hwidLimit$~', $this->input['callback'], $m):
|
|
|
|
|
$this->hwidLimit();
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/toggleHwidLimit(?: (\w+))?$~', $this->input['callback'], $m):
|
|
|
|
|
$this->toggleHwidLimit($m[1] ?? null);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/setHwidDevices(?: (\w+))?$~', $this->input['callback'], $m):
|
|
|
|
|
$this->setHwidDevices($m[1] ?? null);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/hwidUser (\d+)(?:_(\d+))?$~', $this->input['callback'], $m):
|
|
|
|
|
$this->hwidUser($m[1], $m[2] ?? 0);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/hwidUserToggle (\d+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->hwidUserToggle($m[1]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/hwidUserDefault (\d+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->hwidUserDefault($m[1]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/setHwidUserLimit (\d+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->setHwidUserLimit($m[1]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/hwidUserDel (\d+)_(\d+) (.+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->hwidUserDel($m[1], $m[2], $m[3]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/searchLogs (.+)$~', $this->input['message'], $m):
|
|
|
|
|
$this->searchLogs($m[1]);
|
|
|
|
|
break;
|
|
|
|
@@ -291,6 +317,21 @@ class Bot
|
|
|
|
|
case preg_match('~^/logs$~', $this->input['callback'], $m):
|
|
|
|
|
$this->logs();
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/iodine$~', $this->input['callback'], $m):
|
|
|
|
|
$this->iodine();
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/iodineDomain$~', $this->input['callback'], $m):
|
|
|
|
|
$this->iodineDomain();
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/iodinePassword$~', $this->input['callback'], $m):
|
|
|
|
|
$this->iodinePassword();
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/setIodineDomain (\w+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->setIodineDomain($m[1]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/setIodinePassword (\w+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->setIodinePassword($m[1]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/getLog (?P<arg>\d+(?:_(?:-)?\d+)?)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->getLog(...explode('_', $m['arg']));
|
|
|
|
|
break;
|
|
|
|
@@ -432,6 +473,9 @@ class Bot
|
|
|
|
|
case preg_match('~^/download (\d+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->downloadPeer($m[1]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/dw (\w+) (\w+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->dw($m[1], $m[2]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/deloc (\d+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->deloc($m[1]);
|
|
|
|
|
break;
|
|
|
|
@@ -580,9 +624,6 @@ class Bot
|
|
|
|
|
case preg_match('~^/templateAdd (\w+)$~', $this->input['callback'], $m):
|
|
|
|
|
$this->templateAdd($m[1]);
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/generateSecretXray$~', $this->input['callback'], $m):
|
|
|
|
|
$this->generateSecretXray();
|
|
|
|
|
break;
|
|
|
|
|
case preg_match('~^/changeFakeDomain$~', $this->input['callback'], $m):
|
|
|
|
|
$this->changeFakeDomain();
|
|
|
|
|
break;
|
|
|
|
@@ -1691,6 +1732,7 @@ class Bot
|
|
|
|
|
'wg1' => $wg1,
|
|
|
|
|
'ad' => yaml_parse_file($this->adguard),
|
|
|
|
|
'pac' => $this->getPacConf(),
|
|
|
|
|
'hwid' => file_exists($this->hwid) ? (json_decode(file_get_contents($this->hwid), true) ?: []) : [],
|
|
|
|
|
'ssl' => file_exists('/certs/cert_private') && preg_match('~BEGIN PRIVATE KEY~', file_get_contents('/certs/cert_private')) ? [
|
|
|
|
|
'private' => file_get_contents('/certs/cert_private'),
|
|
|
|
|
'public' => file_get_contents('/certs/cert_public'),
|
|
|
|
@@ -1807,6 +1849,13 @@ class Bot
|
|
|
|
|
file_put_contents('/config/mtprotodomain', $json['mtprotodomain'] ?: '');
|
|
|
|
|
$this->restartTG();
|
|
|
|
|
}
|
|
|
|
|
// hwid
|
|
|
|
|
if (array_key_exists('hwid', $json)) {
|
|
|
|
|
$out[] = 'update hwid devices';
|
|
|
|
|
$this->update($this->input['chat'], $this->input['message_id'], implode("\n", $out));
|
|
|
|
|
$data = is_array($json['hwid']) ? $json['hwid'] : [];
|
|
|
|
|
file_put_contents($this->hwid, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
|
|
|
|
}
|
|
|
|
|
// xray
|
|
|
|
|
if (!empty($json['xray'])) {
|
|
|
|
|
$out[] = 'update xray';
|
|
|
|
@@ -1849,6 +1898,17 @@ class Bot
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function dw($u, $t)
|
|
|
|
|
{
|
|
|
|
|
$pac = $this->getPacConf();
|
|
|
|
|
$c = $this->getXray()['inbounds'][0]['settings']['clients'][$u];
|
|
|
|
|
$_GET['s'] = $c['id'];
|
|
|
|
|
$_GET['t'] = $t;
|
|
|
|
|
$_SERVER['SERVER_NAME'] = $this->getDomain($pac['transport'] != 'Reality');
|
|
|
|
|
$conf = $this->subscription(1);
|
|
|
|
|
$this->sendFile($this->input['from'], new CURLStringFile($conf, $c['email'] . ($t == 'cl' ? '_mihomo.yaml' :($t == 'si' ? '_singbox.json' : '_v2ray.json'))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function downloadPeer($client)
|
|
|
|
|
{
|
|
|
|
|
$cl = $client;
|
|
|
|
@@ -4347,6 +4407,104 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
return implode("\n", $result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function iodineDomain()
|
|
|
|
|
{
|
|
|
|
|
$r = $this->send(
|
|
|
|
|
$this->input['chat'],
|
|
|
|
|
"@{$this->input['username']} enter domain",
|
|
|
|
|
$this->input['message_id'],
|
|
|
|
|
reply: 'enter domain',
|
|
|
|
|
);
|
|
|
|
|
$_SESSION['reply'][$r['result']['message_id']] = [
|
|
|
|
|
'start_message' => $this->input['message_id'],
|
|
|
|
|
'callback' => 'setIodineDomain',
|
|
|
|
|
'args' => [],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function iodinePassword()
|
|
|
|
|
{
|
|
|
|
|
$r = $this->send(
|
|
|
|
|
$this->input['chat'],
|
|
|
|
|
"@{$this->input['username']} enter password",
|
|
|
|
|
$this->input['message_id'],
|
|
|
|
|
reply: 'enter password',
|
|
|
|
|
);
|
|
|
|
|
$_SESSION['reply'][$r['result']['message_id']] = [
|
|
|
|
|
'start_message' => $this->input['message_id'],
|
|
|
|
|
'callback' => 'setIodinePassword',
|
|
|
|
|
'args' => [],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setIodinePassword($text)
|
|
|
|
|
{
|
|
|
|
|
$c = $this->getPacConf();
|
|
|
|
|
if ($text) {
|
|
|
|
|
$c['iodinePassword'] = $text;
|
|
|
|
|
} else {
|
|
|
|
|
unset($c['iodinePassword']);
|
|
|
|
|
}
|
|
|
|
|
$this->setPacConf($c);
|
|
|
|
|
$this->iodineRestart();
|
|
|
|
|
$this->iodine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setIodineDomain($text)
|
|
|
|
|
{
|
|
|
|
|
$c = $this->getPacConf();
|
|
|
|
|
if ($text) {
|
|
|
|
|
$c['iodineDomain'] = $text;
|
|
|
|
|
} else {
|
|
|
|
|
unset($c['iodineDomain']);
|
|
|
|
|
}
|
|
|
|
|
$this->setPacConf($c);
|
|
|
|
|
$this->iodineRestart();
|
|
|
|
|
$this->iodine();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function iodineRestart()
|
|
|
|
|
{
|
|
|
|
|
$c = $this->getPacConf();
|
|
|
|
|
$this->ssh('pkill iodine', 'io');
|
|
|
|
|
if (!empty($c['iodineDomain']) && !empty($c['iodinePassword'])) {
|
|
|
|
|
$this->ssh("iodined -c -P {$c['iodinePassword']} 10.0.0.1 {$c['iodineDomain']}", 'io');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function iodine()
|
|
|
|
|
{
|
|
|
|
|
$c = $this->getPacConf();
|
|
|
|
|
$text[] = 'Iodine';
|
|
|
|
|
$text[] = "domain: {$c['iodineDomain']}";
|
|
|
|
|
$text[] = "password: {$c['iodinePassword']}";
|
|
|
|
|
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('set domain'),
|
|
|
|
|
'callback_data' => "/iodineDomain",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('set password'),
|
|
|
|
|
'callback_data' => "/iodinePassword",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('back'),
|
|
|
|
|
'callback_data' => "/menu",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$this->update(
|
|
|
|
|
$this->input['chat'],
|
|
|
|
|
$this->input['message_id'],
|
|
|
|
|
implode("\n", $text),
|
|
|
|
|
$data ?: false,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function menu($type = false, $arg = false, $return = false)
|
|
|
|
|
{
|
|
|
|
|
$conf = $this->getPacConf();
|
|
|
|
@@ -4402,6 +4560,7 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$this->i18n($this->ssh('pgrep mtproto-proxy', 'tg') ? 'on' : 'off') . ' ' . $this->i18n('mtproto'),
|
|
|
|
|
$this->i18n(exec("JSON=1 timeout 2 dnslookup google.com ad") ? 'on' : 'off') . ' ' . $this->i18n('ad_title'),
|
|
|
|
|
$this->i18n($this->ssh('pgrep ssserver', 'ss') ? 'on' : 'off') . ' ' . $this->i18n('sh_title'),
|
|
|
|
|
$this->i18n($this->ssh('pgrep iodine', 'io') ? 'on' : 'off') . ' ' . $this->i18n('Iodine'),
|
|
|
|
|
$this->i18n($this->warpStatus()) . ' ' . $this->i18n('warp'),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
@@ -4413,6 +4572,7 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$this->i18n($c['tg'] ? 'on' : 'off') . ' ' . getenv('TGPORT'),
|
|
|
|
|
$this->i18n($c['ad'] ? 'on' : 'off') . ' 853',
|
|
|
|
|
$this->i18n($c['ss'] ? 'on' : 'off') . ' ' . getenv('SSPORT'),
|
|
|
|
|
$this->i18n($c['io'] ? 'on' : 'off') . ' 53',
|
|
|
|
|
'',
|
|
|
|
|
],
|
|
|
|
|
]);
|
|
|
|
@@ -4486,6 +4646,12 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
'callback_data' => "/pacMenu 0",
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('Iodine'),
|
|
|
|
|
'callback_data' => "/iodine",
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('config'),
|
|
|
|
@@ -4495,7 +4661,7 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
[
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('chat'),
|
|
|
|
|
'url' => base64_decode('aHR0cHM6Ly90Lm1lLytXZnhnNi1ucm9rQmxNbVl5'),
|
|
|
|
|
'url' => base64_decode('aHR0cHM6Ly90Lm1lLys0RzMtUTRkX3ZGRXhPRGN5'),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('donate'),
|
|
|
|
@@ -4597,6 +4763,263 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$this->xray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function hwidLimit()
|
|
|
|
|
{
|
|
|
|
|
$pac = $this->getPacConf();
|
|
|
|
|
$enabled = !empty($pac['hwid_limit_enabled']);
|
|
|
|
|
$count = max(1, (int) ($pac['hwid_device_count'] ?: 1));
|
|
|
|
|
|
|
|
|
|
$text[] = 'Settings -> ' . $this->i18n('hwid limit');
|
|
|
|
|
$text[] = $this->i18n('hwid notice');
|
|
|
|
|
$text[] = $this->i18n('hwid limit') . ': ' . ($enabled ? $count : $this->i18n('off'));
|
|
|
|
|
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n($enabled ? 'on' : 'off'),
|
|
|
|
|
'callback_data' => '/toggleHwidLimit',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('set hwid devices count') . ': ' . $count,
|
|
|
|
|
'callback_data' => '/setHwidDevices',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('back'),
|
|
|
|
|
'callback_data' => '/xray',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$this->update(
|
|
|
|
|
$this->input['chat'],
|
|
|
|
|
$this->input['message_id'],
|
|
|
|
|
implode("\n", $text ?: ['...']),
|
|
|
|
|
$data ?: false,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function toggleHwidLimit($context = null)
|
|
|
|
|
{
|
|
|
|
|
$pac = $this->getPacConf();
|
|
|
|
|
$pac['hwid_limit_enabled'] = $pac['hwid_limit_enabled'] ? 0 : 1;
|
|
|
|
|
if (!empty($pac['hwid_limit_enabled']) && empty($pac['hwid_device_count'])) {
|
|
|
|
|
$pac['hwid_device_count'] = 1;
|
|
|
|
|
}
|
|
|
|
|
$this->setPacConf($pac);
|
|
|
|
|
$this->answer($this->input['callback_id'], $this->i18n('hwid notice'), true);
|
|
|
|
|
if ($context === 'xray') {
|
|
|
|
|
$this->xray();
|
|
|
|
|
} else {
|
|
|
|
|
$this->hwidLimit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setHwidDevices($context = null)
|
|
|
|
|
{
|
|
|
|
|
$r = $this->send(
|
|
|
|
|
$this->input['chat'],
|
|
|
|
|
"@{$this->input['username']} enter hwid devices count",
|
|
|
|
|
$this->input['message_id'],
|
|
|
|
|
reply: 'enter hwid devices count',
|
|
|
|
|
);
|
|
|
|
|
$_SESSION['reply'][$r['result']['message_id']] = [
|
|
|
|
|
'start_message' => $this->input['message_id'],
|
|
|
|
|
'callback' => 'saveHwidDevices',
|
|
|
|
|
'args' => [$context],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function saveHwidDevices($count, $context = null)
|
|
|
|
|
{
|
|
|
|
|
$count = (int) $count;
|
|
|
|
|
if ($count <= 0) {
|
|
|
|
|
$count = 1;
|
|
|
|
|
}
|
|
|
|
|
$pac = $this->getPacConf();
|
|
|
|
|
$pac['hwid_device_count'] = $count;
|
|
|
|
|
$this->setPacConf($pac);
|
|
|
|
|
$this->send($this->input['chat'], $this->i18n('hwid notice'), $this->input['message_id']);
|
|
|
|
|
if ($context === 'xray') {
|
|
|
|
|
$this->xray();
|
|
|
|
|
} else {
|
|
|
|
|
$this->hwidLimit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getHwidStorage()
|
|
|
|
|
{
|
|
|
|
|
if (!file_exists($this->hwid)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
$data = json_decode(file_get_contents($this->hwid), true);
|
|
|
|
|
return is_array($data) ? $data : [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setHwidStorage(array $storage)
|
|
|
|
|
{
|
|
|
|
|
file_put_contents($this->hwid, json_encode($storage, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getHwidDevicesByUser($uid)
|
|
|
|
|
{
|
|
|
|
|
$storage = $this->getHwidStorage();
|
|
|
|
|
return $storage[$uid] ?? [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setHwidDevice($uid, $hwid, array $info)
|
|
|
|
|
{
|
|
|
|
|
$storage = $this->getHwidStorage();
|
|
|
|
|
$storage[$uid][$hwid] = $info;
|
|
|
|
|
$this->setHwidStorage($storage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function deleteHwidDevice($uid, $hwid)
|
|
|
|
|
{
|
|
|
|
|
$storage = $this->getHwidStorage();
|
|
|
|
|
if (isset($storage[$uid][$hwid])) {
|
|
|
|
|
unset($storage[$uid][$hwid]);
|
|
|
|
|
if (empty($storage[$uid])) {
|
|
|
|
|
unset($storage[$uid]);
|
|
|
|
|
}
|
|
|
|
|
$this->setHwidStorage($storage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function deleteHwidUser($uid)
|
|
|
|
|
{
|
|
|
|
|
$storage = $this->getHwidStorage();
|
|
|
|
|
if (isset($storage[$uid])) {
|
|
|
|
|
unset($storage[$uid]);
|
|
|
|
|
$this->setHwidStorage($storage);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function getHwidTokenScope($index)
|
|
|
|
|
{
|
|
|
|
|
return ($this->input['chat'] ?? 'global') . ':' . $index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function rememberHwidToken($scope, $hwid)
|
|
|
|
|
{
|
|
|
|
|
if (!isset($_SESSION['hwidTokens'])) {
|
|
|
|
|
$_SESSION['hwidTokens'] = [];
|
|
|
|
|
}
|
|
|
|
|
if (!isset($_SESSION['hwidTokens'][$scope])) {
|
|
|
|
|
$_SESSION['hwidTokens'][$scope] = [];
|
|
|
|
|
}
|
|
|
|
|
do {
|
|
|
|
|
try {
|
|
|
|
|
$token = bin2hex(random_bytes(5));
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
$token = substr(hash('sha256', $hwid . microtime(true)), 0, 10);
|
|
|
|
|
}
|
|
|
|
|
} while (isset($_SESSION['hwidTokens'][$scope][$token]));
|
|
|
|
|
|
|
|
|
|
$_SESSION['hwidTokens'][$scope][$token] = $hwid;
|
|
|
|
|
|
|
|
|
|
return $token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function resolveHwidToken($scope, $token)
|
|
|
|
|
{
|
|
|
|
|
if (isset($_SESSION['hwidTokens'][$scope][$token])) {
|
|
|
|
|
$hwid = $_SESSION['hwidTokens'][$scope][$token];
|
|
|
|
|
unset($_SESSION['hwidTokens'][$scope][$token]);
|
|
|
|
|
return $hwid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$decoded = base64_decode($token, true);
|
|
|
|
|
|
|
|
|
|
return $decoded !== false ? $decoded : '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function processHwidRequest(array $client)
|
|
|
|
|
{
|
|
|
|
|
$pac = $this->getPacConf();
|
|
|
|
|
if (empty($pac['hwid_limit_enabled']) || !empty($client['hwid_disabled'])) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$limit = (int) ($client['hwid_limit'] ?: ($pac['hwid_device_count'] ?: 0));
|
|
|
|
|
if ($limit <= 0) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$devices = $this->getHwidDevicesByUser($client['id']);
|
|
|
|
|
$hwid = trim($_SERVER['HTTP_X_HWID'] ?? '');
|
|
|
|
|
$isBrowser = $this->isBrowserRequest();
|
|
|
|
|
|
|
|
|
|
if ($hwid === '') {
|
|
|
|
|
if ($isBrowser) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$message = 'HWID device limit exceeded';
|
|
|
|
|
header('announce: base64:' . base64_encode($message));
|
|
|
|
|
header('X-HWID-Status: ' . $message);
|
|
|
|
|
header('HTTP/1.1 429 Too Many Requests', true, 429);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$isNew = !isset($devices[$hwid]);
|
|
|
|
|
|
|
|
|
|
if ($isNew && count($devices) >= $limit) {
|
|
|
|
|
$message = 'HWID device limit exceeded';
|
|
|
|
|
header('announce: base64:' . base64_encode($message));
|
|
|
|
|
header('X-HWID-Status: ' . $message);
|
|
|
|
|
header('HTTP/1.1 429 Too Many Requests', true, 429);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->setHwidDevice($client['id'], $hwid, [
|
|
|
|
|
'time' => time(),
|
|
|
|
|
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
|
|
|
|
|
'device_os' => $_SERVER['HTTP_X_DEVICE_OS'] ?? '',
|
|
|
|
|
'os_version' => $_SERVER['HTTP_X_VER_OS'] ?? '',
|
|
|
|
|
'device_model' => $_SERVER['HTTP_X_DEVICE_MODEL'] ?? '',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected function isBrowserRequest()
|
|
|
|
|
{
|
|
|
|
|
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
|
|
|
|
$accept = $_SERVER['HTTP_ACCEPT'] ?? '';
|
|
|
|
|
|
|
|
|
|
if ($userAgent === '' && $accept === '') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$browserPatterns = [
|
|
|
|
|
'Mozilla/',
|
|
|
|
|
'Chrome/',
|
|
|
|
|
'Safari/',
|
|
|
|
|
'Firefox/',
|
|
|
|
|
'Edge/',
|
|
|
|
|
'Edg/',
|
|
|
|
|
'MSIE ',
|
|
|
|
|
'Trident/',
|
|
|
|
|
'Opera/',
|
|
|
|
|
'OPR/',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
foreach ($browserPatterns as $pattern) {
|
|
|
|
|
if (stripos($userAgent, $pattern) !== false) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stripos($accept, 'text/html') !== false) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function switchSilence()
|
|
|
|
|
{
|
|
|
|
|
$c = $this->getPacConf();
|
|
|
|
@@ -5432,6 +5855,7 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$st = $this->getXrayStats();
|
|
|
|
|
foreach ($r['inbounds'][0]['settings']['clients'] as $k => $v) {
|
|
|
|
|
if ($i == $k) {
|
|
|
|
|
$this->deleteHwidUser($r['inbounds'][0]['settings']['clients'][$k]['id']);
|
|
|
|
|
unset($r['inbounds'][0]['settings']['clients'][$k]);
|
|
|
|
|
unset($st['users'][$k]);
|
|
|
|
|
$this->setXrayStats($st);
|
|
|
|
@@ -5857,9 +6281,6 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
|
|
|
|
|
public function xray($page = 0)
|
|
|
|
|
{
|
|
|
|
|
if (!$this->ssh('pgrep xray', 'xr')) {
|
|
|
|
|
$this->generateSecretXray();
|
|
|
|
|
}
|
|
|
|
|
$c = $this->getXray();
|
|
|
|
|
$p = $this->getPacConf();
|
|
|
|
|
$text[] = "Menu -> " . $this->i18n('xray');
|
|
|
|
@@ -5903,13 +6324,25 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
'callback_data' => "/changeTransport 1",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$ip_count = $p['ip_count'] ?: 1;
|
|
|
|
|
$ip_count = $p['ip_count'] ?: 1;
|
|
|
|
|
$hwidEnabled = !empty($p['hwid_limit_enabled']);
|
|
|
|
|
$defaultHwids = max(1, (int) ($p['hwid_device_count'] ?: 1));
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('ip limit') . ' ' . ($p['ip_limit'] ? ": {$p['ip_limit']} sec & $ip_count" : $this->i18n('off')),
|
|
|
|
|
'callback_data' => "/setIpLimit",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('hwid limit') . ': ' . $this->i18n($hwidEnabled ? 'on' : 'off') . " ({$defaultHwids})",
|
|
|
|
|
'callback_data' => '/toggleHwidLimit xray',
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('set hwid devices count'),
|
|
|
|
|
'callback_data' => '/setHwidDevices xray',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
if ($p['transport'] == 'Reality') {
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
@@ -6295,12 +6728,18 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
|
|
|
|
|
public function userXr($i)
|
|
|
|
|
{
|
|
|
|
|
$c = $this->getXray()['inbounds'][0]['settings']['clients'][$i];
|
|
|
|
|
$xray = $this->getXray();
|
|
|
|
|
$c = $xray['inbounds'][0]['settings']['clients'][$i];
|
|
|
|
|
$pac = $this->getPacConf();
|
|
|
|
|
$domain = $this->getDomain($pac['transport'] != 'Reality');
|
|
|
|
|
$scheme = empty($this->nginxGetTypeCert()) ? 'http' : 'https';
|
|
|
|
|
$hash = $this->getHashBot();
|
|
|
|
|
|
|
|
|
|
$devices = $this->getHwidDevicesByUser($c['id']);
|
|
|
|
|
$hwidEnabled = !empty($pac['hwid_limit_enabled']) && empty($c['hwid_disabled']);
|
|
|
|
|
$defaultHwid = max(1, (int) ($pac['hwid_device_count'] ?: 1));
|
|
|
|
|
$hwidLimit = $c['hwid_limit'] ? (int) $c['hwid_limit'] : $defaultHwid;
|
|
|
|
|
|
|
|
|
|
$text[] = "Menu -> " . $this->i18n('xray') . " -> {$c['email']}\n";
|
|
|
|
|
if (file_exists(__DIR__ . '/subscription.php')) {
|
|
|
|
|
$text[] = "<a href='$scheme://{$domain}/pac$hash/sub?id={$c['id']}'>subscription</a>";
|
|
|
|
@@ -6347,15 +6786,29 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('v2ray'),
|
|
|
|
|
'web_app' => ['url' => "https://{$domain}/pac$hash?t=s&s={$c['id']}"],
|
|
|
|
|
'web_app' => ['url' => "https://{$domain}/pac$hash?t=s&s={$c['id']}"]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('singbox'),
|
|
|
|
|
'web_app' => ['url' => "https://{$domain}/pac$hash?t=si&s={$c['id']}"],
|
|
|
|
|
'web_app' => ['url' => "https://{$domain}/pac$hash?t=si&s={$c['id']}"]
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('mihomo'),
|
|
|
|
|
'web_app' => ['url' => "https://{$domain}/pac$hash?t=cl&s={$c['id']}"],
|
|
|
|
|
'web_app' => ['url' => "https://{$domain}/pac$hash?t=cl&s={$c['id']}"]
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('v2ray ⬇️'),
|
|
|
|
|
'callback_data' => "/dw {$i} s",
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('singbox ⬇️'),
|
|
|
|
|
'callback_data' => "/dw {$i} si",
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('mihomo ⬇️'),
|
|
|
|
|
'callback_data' => "/dw {$i} cl",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
@@ -6399,6 +6852,12 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
'callback_data' => "/qrXray {$i}_2",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('hwid limit') . ': ' . ($hwidEnabled ? $hwidLimit : $this->i18n('off')) . ' (' . count($devices) . ')',
|
|
|
|
|
'callback_data' => "/hwidUser $i",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('rename'),
|
|
|
|
@@ -6423,6 +6882,188 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function hwidUser($i, $page = 0)
|
|
|
|
|
{
|
|
|
|
|
$xray = $this->getXray();
|
|
|
|
|
$client = $xray['inbounds'][0]['settings']['clients'][$i];
|
|
|
|
|
$pac = $this->getPacConf();
|
|
|
|
|
|
|
|
|
|
$devices = $this->getHwidDevicesByUser($client['id']);
|
|
|
|
|
$scope = $this->getHwidTokenScope($i);
|
|
|
|
|
if (!isset($_SESSION['hwidTokens'])) {
|
|
|
|
|
$_SESSION['hwidTokens'] = [];
|
|
|
|
|
}
|
|
|
|
|
$_SESSION['hwidTokens'][$scope] = [];
|
|
|
|
|
uasort($devices, fn($a, $b) => ($b['time'] ?? 0) <=> ($a['time'] ?? 0));
|
|
|
|
|
$hwids = array_keys($devices);
|
|
|
|
|
$perPage = max(1, $this->limit ?: 5);
|
|
|
|
|
$total = count($hwids);
|
|
|
|
|
$pages = max(1, (int) ceil($total / $perPage));
|
|
|
|
|
$page = min(max((int) $page, 0), $pages - 1);
|
|
|
|
|
$hwidsPage = array_slice($hwids, $page * $perPage, $perPage);
|
|
|
|
|
$defaultHwid = max(1, (int) ($pac['hwid_device_count'] ?: 1));
|
|
|
|
|
|
|
|
|
|
$text[] = "Menu -> " . $this->i18n('xray') . " -> {$client['email']} -> " . $this->i18n('hwid devices');
|
|
|
|
|
$text[] = $this->i18n('hwid notice');
|
|
|
|
|
if (empty($pac['hwid_limit_enabled'])) {
|
|
|
|
|
$status = $this->i18n('off');
|
|
|
|
|
} elseif (!empty($client['hwid_disabled'])) {
|
|
|
|
|
$status = $this->i18n('off');
|
|
|
|
|
} elseif (!empty($client['hwid_limit'])) {
|
|
|
|
|
$status = (int) $client['hwid_limit'];
|
|
|
|
|
} else {
|
|
|
|
|
$status = "default($defaultHwid)";
|
|
|
|
|
}
|
|
|
|
|
$text[] = $this->i18n('hwid limit') . ': ' . $status;
|
|
|
|
|
$text[] = $this->i18n('hwid devices') . ': ' . $total;
|
|
|
|
|
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n(!empty($client['hwid_disabled']) ? 'off' : 'on'),
|
|
|
|
|
'callback_data' => "/hwidUserToggle $i",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('set hwid devices count'),
|
|
|
|
|
'callback_data' => "/setHwidUserLimit $i",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
if (!empty($client['hwid_limit'])) {
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('use default hwid limit'),
|
|
|
|
|
'callback_data' => "/hwidUserDefault $i",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($total == 0) {
|
|
|
|
|
$text[] = $this->i18n('no devices');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($hwidsPage as $index => $hwid) {
|
|
|
|
|
$info = $devices[$hwid];
|
|
|
|
|
$number = $page * $perPage + $index + 1;
|
|
|
|
|
$details = array_filter([
|
|
|
|
|
$info['device_os'] ?? '',
|
|
|
|
|
$info['os_version'] ?? '',
|
|
|
|
|
$info['device_model'] ?? '',
|
|
|
|
|
], fn($v) => $v !== '');
|
|
|
|
|
$line = $number . '. <code>' . htmlspecialchars($hwid, ENT_HTML5, 'UTF-8') . '</code>';
|
|
|
|
|
if (!empty($details)) {
|
|
|
|
|
$line .= ' - ' . htmlspecialchars(implode(' ', $details), ENT_HTML5, 'UTF-8');
|
|
|
|
|
}
|
|
|
|
|
if (!empty($info['time'])) {
|
|
|
|
|
$line .= ' (' . date('d.m.Y H:i', $info['time']) . ')';
|
|
|
|
|
}
|
|
|
|
|
$text[] = $line;
|
|
|
|
|
if (!empty($info['user_agent'])) {
|
|
|
|
|
$text[] = 'UA: ' . htmlspecialchars($info['user_agent'], ENT_HTML5, 'UTF-8');
|
|
|
|
|
}
|
|
|
|
|
$token = $this->rememberHwidToken($scope, $hwid);
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => '🗑 ' . $number,
|
|
|
|
|
'callback_data' => "/hwidUserDel {$i}_{$page} $token",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($pages > 1) {
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => '<<',
|
|
|
|
|
'callback_data' => "/hwidUser {$i}_" . ($page - 1 >= 0 ? $page - 1 : $pages - 1),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'text' => ($page + 1) . '/' . $pages,
|
|
|
|
|
'callback_data' => "/hwidUser {$i}_$page",
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'text' => '>>',
|
|
|
|
|
'callback_data' => "/hwidUser {$i}_" . (($page + 1) % $pages),
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data[] = [
|
|
|
|
|
[
|
|
|
|
|
'text' => $this->i18n('back'),
|
|
|
|
|
'callback_data' => "/userXr $i",
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$this->update(
|
|
|
|
|
$this->input['chat'],
|
|
|
|
|
$this->input['message_id'],
|
|
|
|
|
implode("\n", $text ?: ['...']),
|
|
|
|
|
$data ?: false,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function hwidUserToggle($i)
|
|
|
|
|
{
|
|
|
|
|
$xray = $this->getXray();
|
|
|
|
|
if (!empty($xray['inbounds'][0]['settings']['clients'][$i]['hwid_disabled'])) {
|
|
|
|
|
unset($xray['inbounds'][0]['settings']['clients'][$i]['hwid_disabled']);
|
|
|
|
|
} else {
|
|
|
|
|
$xray['inbounds'][0]['settings']['clients'][$i]['hwid_disabled'] = 1;
|
|
|
|
|
}
|
|
|
|
|
file_put_contents('/config/xray.json', json_encode($xray, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
|
|
|
|
$this->answer($this->input['callback_id'], $this->i18n('hwid notice'), true);
|
|
|
|
|
$this->hwidUser($i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function hwidUserDefault($i)
|
|
|
|
|
{
|
|
|
|
|
$xray = $this->getXray();
|
|
|
|
|
unset($xray['inbounds'][0]['settings']['clients'][$i]['hwid_limit']);
|
|
|
|
|
file_put_contents('/config/xray.json', json_encode($xray, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
|
|
|
|
$this->hwidUser($i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setHwidUserLimit($i)
|
|
|
|
|
{
|
|
|
|
|
$r = $this->send(
|
|
|
|
|
$this->input['chat'],
|
|
|
|
|
"@{$this->input['username']} enter hwid devices count",
|
|
|
|
|
$this->input['message_id'],
|
|
|
|
|
reply: 'enter hwid devices count',
|
|
|
|
|
);
|
|
|
|
|
$_SESSION['reply'][$r['result']['message_id']] = [
|
|
|
|
|
'start_message' => $this->input['message_id'],
|
|
|
|
|
'callback' => 'saveHwidUserLimit',
|
|
|
|
|
'args' => [$i],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function saveHwidUserLimit($count, $i)
|
|
|
|
|
{
|
|
|
|
|
$xray = $this->getXray();
|
|
|
|
|
$count = (int) $count;
|
|
|
|
|
if ($count > 0) {
|
|
|
|
|
$xray['inbounds'][0]['settings']['clients'][$i]['hwid_limit'] = $count;
|
|
|
|
|
} else {
|
|
|
|
|
unset($xray['inbounds'][0]['settings']['clients'][$i]['hwid_limit']);
|
|
|
|
|
}
|
|
|
|
|
file_put_contents('/config/xray.json', json_encode($xray, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
|
|
|
|
$this->send($this->input['chat'], $this->i18n('hwid notice'), $this->input['message_id']);
|
|
|
|
|
$this->hwidUser($i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function hwidUserDel($i, $page, $hwid)
|
|
|
|
|
{
|
|
|
|
|
$xray = $this->getXray();
|
|
|
|
|
$uid = $xray['inbounds'][0]['settings']['clients'][$i]['id'];
|
|
|
|
|
$scope = $this->getHwidTokenScope($i);
|
|
|
|
|
$decoded = $this->resolveHwidToken($scope, $hwid);
|
|
|
|
|
if ($decoded !== '') {
|
|
|
|
|
$this->deleteHwidDevice($uid, $decoded);
|
|
|
|
|
}
|
|
|
|
|
$this->hwidUser($i, $page);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getDomain($cdn = false)
|
|
|
|
|
{
|
|
|
|
|
$c = $this->getPacConf();
|
|
|
|
@@ -6441,16 +7082,23 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$scheme = empty($this->nginxGetTypeCert()) ? 'http' : 'https';
|
|
|
|
|
$hash = $this->getHashBot();
|
|
|
|
|
$flag = true;
|
|
|
|
|
$client = null;
|
|
|
|
|
foreach ($xr['inbounds'][0]['settings']['clients'] as $k => $v) {
|
|
|
|
|
if ($v['id'] == $_GET['id']) {
|
|
|
|
|
if (empty($v['off'])) {
|
|
|
|
|
$flag = false;
|
|
|
|
|
}
|
|
|
|
|
$uid = $v['id'];
|
|
|
|
|
$email = $v['email'];
|
|
|
|
|
$uid = $v['id'];
|
|
|
|
|
$email = $v['email'];
|
|
|
|
|
$expire = $v['time'];
|
|
|
|
|
$client = $v;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!$flag && !$this->processHwidRequest($client)) {
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
$suburl = "<a href='$scheme://{$domain}/pac$hash/sub?id={$uid}'>subscription</a>";
|
|
|
|
|
$download = $this->getBytes($st['users'][$k]['global']['download'] + $st['users'][$k]['session']['download']);
|
|
|
|
|
$upload = $this->getBytes($st['users'][$k]['global']['upload'] + $st['users'][$k]['session']['upload']);
|
|
|
|
|
$singbox = "$scheme://{$domain}/pac$hash/" . base64_encode(serialize([
|
|
|
|
@@ -6502,6 +7150,7 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$hash = $this->getHashBot();
|
|
|
|
|
|
|
|
|
|
$flag = true;
|
|
|
|
|
$client = null;
|
|
|
|
|
foreach ($xr['inbounds'][0]['settings']['clients'] as $k => $v) {
|
|
|
|
|
if ($v['id'] == $_GET['s']) {
|
|
|
|
|
if (empty($v['off'])) {
|
|
|
|
@@ -6510,6 +7159,7 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$template = base64_decode($v["{$type}template"]);
|
|
|
|
|
$uid = $v['id'];
|
|
|
|
|
$email = $v['email'];
|
|
|
|
|
$client = $v;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -6518,6 +7168,10 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$return && !$this->processHwidRequest($client)) {
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($_GET['r'])) {
|
|
|
|
|
$si = "$scheme://{$domain}/pac$hash/" . base64_encode(serialize([
|
|
|
|
|
'h' => $hash,
|
|
|
|
@@ -6642,7 +7296,6 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'si':
|
|
|
|
|
$c['outbounds'][$index]['server'] = '~domain~';
|
|
|
|
|
$c['outbounds'][$index]['uuid'] = '~uid~';
|
|
|
|
|
if ($pac['transport'] != 'Reality') {
|
|
|
|
|
unset($c['outbounds'][$index]['tls']['reality']);
|
|
|
|
@@ -6725,6 +7378,9 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
}
|
|
|
|
|
$c['route']['rules'] = array_values($c['route']['rules']);
|
|
|
|
|
}
|
|
|
|
|
if (empty($c['route'])) {
|
|
|
|
|
unset($c['route']);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'cl':
|
|
|
|
|
$c = $this->addClashRuleSet($c);
|
|
|
|
@@ -6948,7 +7604,9 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$route['rules'] = array_values($route['rules']);
|
|
|
|
|
if (!empty($route['rules'])) {
|
|
|
|
|
$route['rules'] = array_values($route['rules']);
|
|
|
|
|
}
|
|
|
|
|
$route['rule_set'] = array_merge($route['rule_set'] ?: [], $ruleset ?: []);
|
|
|
|
|
if (empty($route['rule_set'])) {
|
|
|
|
|
unset($route['rule_set']);
|
|
|
|
@@ -6961,25 +7619,6 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
return json_decode(file_get_contents('/config/xray.json'), true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function generateSecretXray()
|
|
|
|
|
{
|
|
|
|
|
$c = $this->getXray();
|
|
|
|
|
$shortId = trim($this->ssh('openssl rand -hex 8', 'xr'));
|
|
|
|
|
$keys = $this->ssh('xray x25519', 'xr');
|
|
|
|
|
preg_match('~^Private key:\s([^\s]+)~m', $keys, $m);
|
|
|
|
|
$private = trim($m[1]);
|
|
|
|
|
preg_match('~^Public key:\s([^\s]+)~m', $keys, $m);
|
|
|
|
|
$public = trim($m[1]);
|
|
|
|
|
$c['inbounds'][0]['streamSettings']['realitySettings']['privateKey'] = $private;
|
|
|
|
|
$c['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0] = $shortId;
|
|
|
|
|
$pac = $this->getPacConf();
|
|
|
|
|
$pac['xray'] = $public;
|
|
|
|
|
$pac['reality']['shortId'] = $shortId;
|
|
|
|
|
$pac['reality']['privateKey'] = $private;
|
|
|
|
|
$this->setPacConf($pac);
|
|
|
|
|
$this->restartXray($c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setUpstreamDomain($domain)
|
|
|
|
|
{
|
|
|
|
|
$nginx = file_get_contents('/config/upstream.conf');
|
|
|
|
@@ -7542,6 +8181,10 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
'text' => $this->i18n($c['ss'] ? 'on' : 'off') . ' 8388 Shadowsocks',
|
|
|
|
|
'callback_data' => "/hidePort ss",
|
|
|
|
|
]],
|
|
|
|
|
[[
|
|
|
|
|
'text' => $this->i18n($c['io'] ? 'on' : 'off') . ' 53 Iodine',
|
|
|
|
|
'callback_data' => "/hidePort io",
|
|
|
|
|
]],
|
|
|
|
|
];
|
|
|
|
|
if (!empty($pac['restart'])) {
|
|
|
|
|
$data[] = [
|
|
|
|
@@ -7573,6 +8216,7 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
'tg' => getenv('TGPORT') . ':' . getenv('TGPORT'),
|
|
|
|
|
'ad' => '853:853',
|
|
|
|
|
'ss' => '8388:8388',
|
|
|
|
|
'io' => '53:53/udp',
|
|
|
|
|
];
|
|
|
|
|
$f = '/docker/compose';
|
|
|
|
|
$c = yaml_parse_file($f);
|
|
|
|
@@ -7842,6 +8486,17 @@ DNS-over-HTTPS with IP:
|
|
|
|
|
$p['reality']['domain'] = $p['reality']['domain'] ?: 'web.telegram.org';
|
|
|
|
|
$p['reality']['destination'] = $p['reality']['destination'] ?: $p['reality']['domain'] . ':443';
|
|
|
|
|
$p['transport'] = $ws ? 'Websocket' : 'Reality';
|
|
|
|
|
if (empty($p['xray'])) {
|
|
|
|
|
$shortId = trim($this->ssh('openssl rand -hex 8', 'xr'));
|
|
|
|
|
$keys = $this->ssh('xray x25519', 'xr');
|
|
|
|
|
preg_match('~^PrivateKey:\s([^\s]+)~m', $keys, $m);
|
|
|
|
|
$private = trim($m[1]);
|
|
|
|
|
preg_match('~^Password:\s([^\s]+)~m', $keys, $m);
|
|
|
|
|
$public = trim($m[1]);
|
|
|
|
|
$p['xray'] = $public;
|
|
|
|
|
$p['reality']['shortId'] = $shortId;
|
|
|
|
|
$p['reality']['privateKey'] = $private;
|
|
|
|
|
}
|
|
|
|
|
if (!empty($ws)) {
|
|
|
|
|
$p['reality']['domain'] = $x['inbounds'][0]['streamSettings']['realitySettings']['serverNames'][0] ?: $p['reality']['domain'];
|
|
|
|
|
$p['reality']['destination'] = $x['inbounds'][0]['streamSettings']['realitySettings']['dest'] ?: $p['reality']['destination'];
|
|
|
|
|