Skip to content

Commit bd3c231

Browse files
committed
fix: reuse local path from local-mode Bot API server instead of downloading
1 parent 2e14309 commit bd3c231

2 files changed

Lines changed: 65 additions & 9 deletions

File tree

astrbot/core/platform/sources/telegram/tg_adapter.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -439,17 +439,22 @@ async def message_handler(
439439
async def _download_to_temp(self, file_path: str) -> str:
440440
"""Download a Telegram file to a local temp path.
441441
442-
Telegram's file_path can be a relative path (self-hosted Bot API server
443-
or reverse proxy setups), and the generic message components only
444-
understand local paths or http(s) URLs. So we always fetch the file
445-
ourselves instead of passing file_path straight into the component.
442+
On the cloud Bot API (and non-local self-hosted servers), get_file()
443+
returns an http(s) URL, which we fetch into a temp file so downstream
444+
components get a readable local path. But when the Bot API server runs
445+
in local mode, get_file() returns an absolute local path instead; that
446+
file already lives on disk, so we return it as-is rather than feeding a
447+
local path into download_file() (an aiohttp GET, which cannot resolve
448+
it).
446449
447450
Args:
448451
file_path: The file_path returned by Telegram's getFile API.
449452
450453
Returns:
451-
Local absolute path of the downloaded file.
454+
Local absolute path of the file.
452455
"""
456+
if os.path.isfile(file_path):
457+
return file_path
453458
file_basename = os.path.basename(file_path)
454459
temp_dir = get_astrbot_temp_path()
455460
temp_path = os.path.join(temp_dir, f"{uuid.uuid4().hex}_{file_basename}")

tests/test_telegram_adapter.py

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,60 @@ async def test_telegram_video_caption_populates_message_text_and_plain(tmp_path)
231231
)
232232

233233

234+
@pytest.mark.asyncio
235+
async def test_download_to_temp_returns_local_path_without_downloading(tmp_path):
236+
"""#9448: a local-mode Bot API server returns an absolute local path, which
237+
should be reused as-is instead of being downloaded as a URL."""
238+
TelegramPlatformAdapter = _load_telegram_adapter()
239+
adapter = TelegramPlatformAdapter(
240+
make_platform_config("telegram"),
241+
{},
242+
asyncio.Queue(),
243+
)
244+
local_file = tmp_path / "photos" / "file_10.jpg"
245+
local_file.parent.mkdir(parents=True)
246+
local_file.write_bytes(b"local-bytes")
247+
convert_message_globals = adapter._download_to_temp.__func__.__globals__
248+
mock_download = AsyncMock()
249+
250+
with patch.dict(convert_message_globals, {"download_file": mock_download}):
251+
result = await adapter._download_to_temp(str(local_file))
252+
253+
assert result == str(local_file)
254+
mock_download.assert_not_awaited()
255+
256+
257+
@pytest.mark.asyncio
258+
async def test_download_to_temp_downloads_remote_url(tmp_path):
259+
"""A remote URL should still be downloaded to the temp dir, not returned as-is."""
260+
TelegramPlatformAdapter = _load_telegram_adapter()
261+
adapter = TelegramPlatformAdapter(
262+
make_platform_config("telegram"),
263+
{},
264+
asyncio.Queue(),
265+
)
266+
url = "https://api.telegram.org/file/bot123/photos/file_10.jpg"
267+
convert_message_globals = adapter._download_to_temp.__func__.__globals__
268+
mock_download = AsyncMock()
269+
270+
with patch.dict(
271+
convert_message_globals,
272+
{
273+
"get_astrbot_temp_path": MagicMock(return_value=str(tmp_path)),
274+
"download_file": mock_download,
275+
},
276+
):
277+
result = await adapter._download_to_temp(url)
278+
279+
assert result.startswith(str(tmp_path))
280+
assert result != url
281+
mock_download.assert_awaited_once()
282+
assert mock_download.await_args.args[0] == url
283+
284+
234285
@pytest.mark.asyncio
235286
async def test_telegram_document_downloads_to_local_temp_path(tmp_path):
236-
"""#9448: document 组件必须拿到本地路径,而不是原始 Telegram file_path/URL"""
287+
"""#9448: the document component must hold a local path, not the raw Telegram file_path/URL."""
237288
TelegramPlatformAdapter = _load_telegram_adapter()
238289
adapter = TelegramPlatformAdapter(
239290
make_platform_config("telegram"),
@@ -269,7 +320,7 @@ async def test_telegram_document_downloads_to_local_temp_path(tmp_path):
269320

270321
@pytest.mark.asyncio
271322
async def test_telegram_video_downloads_to_local_temp_path(tmp_path):
272-
"""#9448: video 组件必须拿到本地路径,而不是原始 Telegram file_path/URL"""
323+
"""#9448: the video component must hold a local path, not the raw Telegram file_path/URL."""
273324
TelegramPlatformAdapter = _load_telegram_adapter()
274325
adapter = TelegramPlatformAdapter(
275326
make_platform_config("telegram"),
@@ -300,7 +351,7 @@ async def test_telegram_video_downloads_to_local_temp_path(tmp_path):
300351

301352
@pytest.mark.asyncio
302353
async def test_telegram_photo_downloads_to_local_temp_path(tmp_path):
303-
"""#9448: photo 组件必须拿到本地路径,而不是原始 Telegram file_path/URL"""
354+
"""#9448: the photo component must hold a local path, not the raw Telegram file_path/URL."""
304355
TelegramPlatformAdapter = _load_telegram_adapter()
305356
adapter = TelegramPlatformAdapter(
306357
make_platform_config("telegram"),
@@ -329,7 +380,7 @@ async def test_telegram_photo_downloads_to_local_temp_path(tmp_path):
329380

330381
@pytest.mark.asyncio
331382
async def test_telegram_sticker_downloads_to_local_temp_path(tmp_path):
332-
"""#9448: sticker 组件必须拿到本地路径,而不是原始 Telegram file_path/URL"""
383+
"""#9448: the sticker component must hold a local path, not the raw Telegram file_path/URL."""
333384
TelegramPlatformAdapter = _load_telegram_adapter()
334385
adapter = TelegramPlatformAdapter(
335386
make_platform_config("telegram"),

0 commit comments

Comments
 (0)