diff --git a/lnbits/core/services/notifications.py b/lnbits/core/services/notifications.py index fba38b42f..ddecfb11c 100644 --- a/lnbits/core/services/notifications.py +++ b/lnbits/core/services/notifications.py @@ -74,7 +74,7 @@ async def send_admin_notification( message: str, message_type: str | None = None, ) -> None: - return await send_notification( + return await send_notification_in_background( settings.lnbits_telegram_notifications_chat_id, settings.lnbits_nostr_notifications_identifiers, settings.lnbits_email_notifications_to_emails, @@ -97,7 +97,7 @@ async def send_user_notification( if user_notifications.nostr_identifier else [] ) - return await send_notification( + return await send_notification_in_background( user_notifications.telegram_chat_id, nostr_identifiers, email_address, @@ -302,6 +302,27 @@ def send_payment_notification_in_background(wallet: Wallet, payment: Payment): logger.warning(f"Error sending payment notification: {e}") +async def send_notification_in_background( + telegram_chat_id: str | None, + nostr_identifiers: list[str] | None, + email_addresses: list[str] | None, + message: str, + message_type: str | None = None, +): + try: + create_task( + send_notification( + telegram_chat_id, + nostr_identifiers, + email_addresses, + message, + message_type, + ) + ) + except Exception as e: + logger.warning(f"Error sending notification in background: {e}") + + async def send_ws_payment_notification(wallet: Wallet, payment: Payment): # TODO: websocket message should be a clean payment model # await websocket_manager.send(wallet.inkey, payment.json()) diff --git a/tests/unit/test_services_notifications.py b/tests/unit/test_services_notifications.py index d379f2478..e3699fb1f 100644 --- a/tests/unit/test_services_notifications.py +++ b/tests/unit/test_services_notifications.py @@ -39,6 +39,7 @@ from lnbits.core.services.notifications import ( send_nostr_notification, send_nostr_notifications, send_notification, + send_notification_in_background, send_payment_notification, send_payment_push_notification, send_push_notification, @@ -117,7 +118,7 @@ async def test_send_admin_and_user_notification_use_expected_targets( settings: Settings, mocker: MockerFixture ): send_mock = mocker.patch( - "lnbits.core.services.notifications.send_notification", + "lnbits.core.services.notifications.send_notification_in_background", mocker.AsyncMock(), ) original_chat_id = settings.lnbits_telegram_notifications_chat_id @@ -159,6 +160,45 @@ async def test_send_admin_and_user_notification_use_expected_targets( ) +@pytest.mark.anyio +async def test_send_notification_in_background_schedules_notification( + mocker: MockerFixture, +): + scheduled = [] + + def create_task(coro): + scheduled.append(coro) + coro.close() + return mocker.Mock() + + create_task_mock = mocker.patch( + "lnbits.core.services.notifications.create_task", + side_effect=create_task, + ) + send_mock = mocker.patch( + "lnbits.core.services.notifications.send_notification", + mocker.AsyncMock(), + ) + + await send_notification_in_background( + "chat-id", + ["alice@example.com"], + ["admin@example.com"], + "hello", + "settings_update", + ) + + create_task_mock.assert_called_once() + send_mock.assert_called_once_with( + "chat-id", + ["alice@example.com"], + ["admin@example.com"], + "hello", + "settings_update", + ) + assert len(scheduled) == 1 + + @pytest.mark.anyio async def test_send_notification_uses_available_channels_and_swallows_exceptions( settings: Settings, mocker: MockerFixture