diff --git a/app/Http/Controllers/Admin/PluginSessionController.php b/app/Http/Controllers/Admin/PluginSessionController.php index 24176f3..0777e3e 100644 --- a/app/Http/Controllers/Admin/PluginSessionController.php +++ b/app/Http/Controllers/Admin/PluginSessionController.php @@ -103,7 +103,7 @@ class PluginSessionController extends Controller $python = config('coruna.tdata_python', base_path('channel-builder/.venv-tdata/bin/python')); $script = config('coruna.tdata_script', base_path('channel-builder/tools/tglib_to_tdata.py')); - if (! is_file($python) || ! is_file($script)) { + if (! $this->conversionFileExists($python) || ! $this->conversionFileExists($script)) { return response()->json([ 'code' => 1, 'msg' => '转换环境未配置(缺少 Python 或脚本)', @@ -122,10 +122,12 @@ class PluginSessionController extends Controller $exit = -1; @exec($cmd, $output, $exit); - if ($exit !== 0 || ! is_file($zipPath)) { + if ($exit !== 0 || ! $this->conversionFileExists($zipPath)) { $msg = implode("\n", $output) ?: "转换失败 (exit=$exit)"; @unlink($jsonPath); - if (is_file($zipPath)) @unlink($zipPath); + if ($this->conversionFileExists($zipPath)) { + @unlink($zipPath); + } @rmdir($tmpDir); return response()->json(['code' => 1, 'msg' => $msg], 500); @@ -181,7 +183,7 @@ class PluginSessionController extends Controller $python = config('coruna.tdata_python', base_path('channel-builder/.venv-tdata/bin/python')); $script = config('coruna.session_script', base_path('channel-builder/tools/tglib_to_session_files.py')); - if (! is_file($python) || ! is_file($script)) { + if (! $this->conversionFileExists($python) || ! $this->conversionFileExists($script)) { return response()->json([ 'code' => 1, 'msg' => '转换环境未配置(缺少 Python 或脚本)', @@ -268,6 +270,19 @@ class PluginSessionController extends Controller }, $filename, ['Content-Type' => 'text/plain; charset=UTF-8']); } + /** + * is_file() throws when open_basedir blocks a symlink target; never + * leak that as an HTML 500 to the sessions page. + */ + private function conversionFileExists(string $path): bool + { + try { + return is_file($path); + } catch (\Throwable) { + return false; + } + } + /** * Recursively remove a directory (best-effort). */ diff --git a/channel-builder/tools/tglib_to_tdata.py b/channel-builder/tools/tglib_to_tdata.py index 4aed65f..3048ba1 100644 --- a/channel-builder/tools/tglib_to_tdata.py +++ b/channel-builder/tools/tglib_to_tdata.py @@ -64,14 +64,19 @@ def make_session(tmpdir: str, auth_key: bytes, dc_id: int) -> str: return path + ".session" -def convert_to_tdata(session_file: str, out_dir: str): +def convert_to_tdata(session_file: str, out_dir: str, user_id: int): """Use opentele-ng to convert Telethon session → tdata folder (offline).""" from opentele.td import TDesktop from opentele.tl import TelegramClient from opentele.api import UseCurrentSession + if not user_id: + raise ValueError("missing user_id (peerId); cannot convert offline") + async def _run(): client = TelegramClient(session_file) + # Seed UserId so opentele UseCurrentSession skips get_me() / network. + client.UserId = int(user_id) try: tdesk = await client.ToTDesktop(flag=UseCurrentSession) if not tdesk.isLoaded(): @@ -80,7 +85,10 @@ def convert_to_tdata(session_file: str, out_dir: str): shutil.rmtree(out_dir) tdesk.SaveTData(out_dir) finally: - await client.disconnect() + try: + await client.disconnect() + except Exception: + pass asyncio.run(_run()) @@ -108,7 +116,7 @@ def main(): try: session_file = make_session(tmpdir, auth_key, dc_id) tdata_dir = os.path.join(tmpdir, "tdata") - convert_to_tdata(session_file, tdata_dir) + convert_to_tdata(session_file, tdata_dir, user_id) zip_tdata(tdata_dir, output_zip) print(f"wrote {output_zip} ({os.path.getsize(output_zip)} bytes)", file=sys.stderr) finally: