From f1cd57991ebfb7afba11b7119a993e2e445760b1 Mon Sep 17 00:00:00 2001 From: mercury Date: Sun, 7 Apr 2024 13:50:18 +0400 Subject: [PATCH] multiple users for xtls --- app/bot.php | 174 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 147 insertions(+), 27 deletions(-) diff --git a/app/bot.php b/app/bot.php index 1e6ef1a..d64826b 100644 --- a/app/bot.php +++ b/app/bot.php @@ -221,6 +221,12 @@ class Bot case preg_match('~^/addOcUser$~', $this->input['callback'], $m): $this->addOcUser(); break; + case preg_match('~^/addXrUser$~', $this->input['callback'], $m): + $this->addXrUser(); + break; + case preg_match('~^/renameXrUser (\d+)$~', $this->input['callback'], $m): + $this->renameXrUser($m[1]); + break; case preg_match('~^/v2ray$~', $this->input['callback'], $m): $this->v2ray(); break; @@ -269,6 +275,12 @@ class Bot case preg_match('~^/deloc (\d+)$~', $this->input['callback'], $m): $this->deloc($m[1]); break; + case preg_match('~^/userXr (\d+)$~', $this->input['callback'], $m): + $this->userXr($m[1]); + break; + case preg_match('~^/delxr (\d+)$~', $this->input['callback'], $m): + $this->delxr($m[1]); + break; case preg_match('~^/switchTorrent (\d+)$~', $this->input['callback'], $m): $this->switchTorrent($m[1]); break; @@ -294,8 +306,8 @@ class Bot case preg_match('~^/qrSS$~', $this->input['callback'], $m): $this->qrSS(); break; - case preg_match('~^/qrXray$~', $this->input['callback'], $m): - $this->qrXray(); + case preg_match('~^/qrXray (\d+)$~', $this->input['callback'], $m): + $this->qrXray($m[1]); break; case preg_match('~^/qrMtproto$~', $this->input['callback'], $m): $this->qrMtproto(); @@ -663,6 +675,38 @@ class Bot ]; } + public function addXrUser() + { + $r = $this->send( + $this->input['chat'], + "@{$this->input['username']} enter name", + $this->input['message_id'], + reply: 'enter password', + ); + $_SESSION['reply'][$r['result']['message_id']] = [ + 'start_message' => $this->input['message_id'], + 'start_callback' => $this->input['callback_id'], + 'callback' => 'addxrus', + 'args' => [], + ]; + } + + public function renameXrUser($i) + { + $r = $this->send( + $this->input['chat'], + "@{$this->input['username']} enter name", + $this->input['message_id'], + reply: 'enter password', + ); + $_SESSION['reply'][$r['result']['message_id']] = [ + 'start_message' => $this->input['message_id'], + 'start_callback' => $this->input['callback_id'], + 'callback' => 'renXrUs', + 'args' => [$i], + ]; + } + public function restartOcserv($conf) { file_put_contents('/config/ocserv.conf', $conf); @@ -1039,7 +1083,7 @@ class Bot ] : false, 'mtproto' => file_get_contents('/config/mtprotosecret'), 'mtprotodomain' => file_get_contents('/config/mtprotodomain'), - 'xray' => json_decode(file_get_contents('/config/xray.json'), true), + 'xray' => $this->getXray(), 'oc' => file_get_contents('/config/ocserv.conf'), 'ocu' => file_get_contents('/config/ocserv.passwd'), @@ -1407,9 +1451,9 @@ class Bot } } - public function qrXray() + public function qrXray($i) { - $link = $this->linkXray(); + $link = $this->linkXray($i); $qr_file = __DIR__ . "/qr/xray.png"; exec("qrencode -t png -o $qr_file '$link'"); $r = $this->sendPhoto( @@ -3201,12 +3245,12 @@ DNS-over-HTTPS with IP: $this->answer($this->input['callback_id'], 'in developing...'); } - public function linkXray() + public function linkXray($i) { $c = $this->getXray(); $pac = $this->getPacConf(); $domain = $pac['domain'] ?: $this->ip; - return "vless://{$c['inbounds'][0]['settings']['clients'][0]['id']}@$domain:443?security=reality&sni={$c['inbounds'][0]['streamSettings']['realitySettings']['serverNames'][0]}&fp=chrome&pbk={$pac['xray']}&sid={$c['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0]}&type=tcp&flow=xtls-rprx-vision#vpnbot"; + return "vless://{$c['inbounds'][0]['settings']['clients'][$i]['id']}@$domain:443?security=reality&sni={$c['inbounds'][0]['streamSettings']['realitySettings']['serverNames'][0]}&fp=chrome&pbk={$pac['xray']}&sid={$c['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0]}&type=tcp&flow=xtls-rprx-vision#vpnbot"; } public function naiveMenu() @@ -3368,6 +3412,19 @@ DNS-over-HTTPS with IP: $this->menu('oc'); } + public function delxr($i) + { + $r = $this->getXray(); + foreach ($r['inbounds'][0]['settings']['clients'] as $k => $v) { + if ($i == $k) { + unset($r['inbounds'][0]['settings']['clients'][$k]); + $this->restartXray($r); + break; + } + } + $this->xray(); + } + public function getClientsOc() { $users = array_filter(explode("\n", file_get_contents('/config/ocserv.passwd')), fn ($e) => !empty($e)); @@ -3381,26 +3438,44 @@ DNS-over-HTTPS with IP: $this->menu('oc'); } + public function addxrus($user) + { + $c = $this->getXray(); + $uuid = trim($this->ssh('xray uuid', 'xr')); + $c['inbounds'][0]['settings']['clients'][] = [ + 'id' => $uuid, + 'flow' => 'xtls-rprx-vision', + 'email' => $user, + ]; + $this->restartXray($c); + $this->xray(); + } + + public function renXrUs($name, $i) + { + $c = $this->getXray(); + $c['inbounds'][0]['settings']['clients'][$i]['email'] = $name; + $this->restartXray($c); + $this->userXr($i); + } + public function xray() { - $c = $this->getXray(); - $pac = $this->getPacConf(); - $domain = $pac['domain'] ?: $this->ip; - $st = $this->ssh('pgrep xray', 'xr') ? 'on' : 'off'; - $text[] = "Menu -> " . $this->i18n('xray') . "\n"; - $text[] = "uuid: {$c['inbounds'][0]['settings']['clients'][0]['id']}"; - $text[] = "shortId: {$c['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0]}"; - $text[] = "pubkey: {$pac['xray']}"; - $text[] = "fake domain: {$c['inbounds'][0]['streamSettings']['realitySettings']['serverNames'][0]}"; - if ($c['inbounds'][0]['settings']['clients'][0]['id']) { - $text[] = "\n{$this->linkXray()}"; + if (!$this->ssh('pgrep xray', 'xr')) { + $this->generateSecretXray(); } - $text[] = "\nstatus: $st"; + $c = $this->getXray(); + $text[] = "Menu -> " . $this->i18n('xray'); + $text[] = "fake domain: {$c['inbounds'][0]['streamSettings']['realitySettings']['serverNames'][0]}"; $data[] = [ [ - 'text' => $this->i18n('generateSecret'), - 'callback_data' => "/generateSecretXray", + 'text' => 'android', + 'web_app' => ['url' => 'https://github.com/2dust/v2rayNG/releases'], + ], + [ + 'text' => 'windows', + 'web_app' => ['url' => 'https://github.com/2dust/v2rayN/releases'], ], ]; $data[] = [ @@ -3413,11 +3488,17 @@ DNS-over-HTTPS with IP: 'callback_data' => "/selfFakeDomain", ], ]; - if ($c['inbounds'][0]['settings']['clients'][0]['id']) { + $data[] = [ + [ + 'text' => $this->i18n('add peer'), + 'callback_data' => "/addXrUser", + ], + ]; + foreach ($c['inbounds'][0]['settings']['clients'] as $k => $v) { $data[] = [ [ - 'text' => $this->i18n('show QR'), - 'callback_data' => "/qrXray", + 'text' => "{$v['email']}", + 'callback_data' => "/userXr $k", ], ]; } @@ -3435,6 +3516,48 @@ DNS-over-HTTPS with IP: ); } + public function userXr($i) + { + $c = $this->getXray(); + $pac = $this->getPacConf(); + $text[] = "Menu -> " . $this->i18n('xray') . " -> {$c['inbounds'][0]['settings']['clients'][$i]['email']}\n"; + // $text[] = "uuid: {$c['inbounds'][0]['settings']['clients'][$i]['id']}"; + // $text[] = "shortId: {$c['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0]}"; + // $text[] = "pubkey: {$pac['xray']}"; + $text[] = "{$this->linkXray($i)}"; + + $data[] = [ + [ + 'text' => $this->i18n('show QR'), + 'callback_data' => "/qrXray $i", + ], + ]; + $data[] = [ + [ + 'text' => $this->i18n('rename'), + 'callback_data' => "/renameXrUser $i", + ], + ]; + $data[] = [ + [ + 'text' => $this->i18n('delete'), + 'callback_data' => "/delxr $i", + ], + ]; + $data[] = [ + [ + 'text' => $this->i18n('back'), + 'callback_data' => "/xray", + ], + ]; + $this->update( + $this->input['chat'], + $this->input['message_id'], + implode("\n", $text ?: ['...']), + $data ?: false, + ); + } + public function getXray() { return json_decode(file_get_contents('/config/xray.json'), true); @@ -3443,21 +3566,18 @@ DNS-over-HTTPS with IP: public function generateSecretXray() { $c = $this->getXray(); - $uuid = trim($this->ssh('xray uuid', 'xr')); $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]['settings']['clients'][0]['id'] = $uuid; $c['inbounds'][0]['streamSettings']['realitySettings']['privateKey'] = $private; $c['inbounds'][0]['streamSettings']['realitySettings']['shortIds'][0] = $shortId; $pac = $this->getPacConf(); $pac['xray'] = $public; $this->setPacConf($pac); $this->restartXray($c); - $this->xray(); } public function setUpstreamDomain($domain)