diff --git a/nanobot/channels/feishu.py b/nanobot/channels/feishu.py index f617b93db..7e65b6210 100644 --- a/nanobot/channels/feishu.py +++ b/nanobot/channels/feishu.py @@ -1081,8 +1081,9 @@ class FeishuChannel(BaseChannel): if data and filename: file_path = media_dir / filename file_path.write_bytes(data) - logger.debug("Downloaded {} to {}", msg_type, file_path) - return str(file_path), f"[{msg_type}: {filename}]" + path_str = str(file_path) + logger.debug("Downloaded {} to {}", msg_type, path_str) + return path_str, f"[{msg_type}: {path_str}]" return None, f"[{msg_type}: download failed]" diff --git a/tests/channels/test_feishu_reply.py b/tests/channels/test_feishu_reply.py index 430e5abea..31d3a1d71 100644 --- a/tests/channels/test_feishu_reply.py +++ b/tests/channels/test_feishu_reply.py @@ -445,6 +445,58 @@ async def test_on_message_no_extra_api_call_when_no_parent_id() -> None: assert len(captured) == 1 +# --------------------------------------------------------------------------- +# Inbound media tests +# --------------------------------------------------------------------------- + + +@pytest.mark.asyncio +async def test_on_message_audio_publishes_downloaded_path_and_transcription() -> None: + channel = _make_feishu_channel() + channel._processed_message_ids.clear() + captured = [] + + async def capture(msg): + captured.append(msg) + + channel.bus.publish_inbound = capture + channel._download_and_save_media = AsyncMock( + return_value=(r"C:\\Users\\dodre\\.nanobot\\media\\feishu\\voice.ogg", "[audio: voice.ogg]") + ) + channel.transcribe_audio = AsyncMock(return_value="hello from voice") + channel._add_reaction = AsyncMock(return_value=None) + + event = _make_feishu_event( + msg_type="audio", + content='{"file_key": "audio_key", "duration": 1000}', + message_id="om_audio", + ) + await channel._on_message(event) + + channel._download_and_save_media.assert_awaited_once_with( + "audio", {"file_key": "audio_key", "duration": 1000}, "om_audio" + ) + channel.transcribe_audio.assert_awaited_once_with(r"C:\\Users\\dodre\\.nanobot\\media\\feishu\\voice.ogg") + assert len(captured) == 1 + assert captured[0].media == [r"C:\\Users\\dodre\\.nanobot\\media\\feishu\\voice.ogg"] + assert captured[0].content == "[transcription: hello from voice]" + + +@pytest.mark.asyncio +async def test_download_and_save_media_returns_absolute_path_in_content(monkeypatch, tmp_path) -> None: + channel = _make_feishu_channel() + monkeypatch.setattr(feishu, "get_media_dir", lambda _channel: tmp_path) + channel._download_file_sync = MagicMock(return_value=(b"voice-bytes", None)) + + file_path, content_text = await channel._download_and_save_media( + "audio", {"file_key": "voice_key"}, "om_audio" + ) + + assert file_path == str(tmp_path / "voice_key.ogg") + assert (tmp_path / "voice_key.ogg").read_bytes() == b"voice-bytes" + assert content_text == f"[audio: {file_path}]" + + # --------------------------------------------------------------------------- # Session key derivation tests # ---------------------------------------------------------------------------