Stage 07.3.5 — WebSocket Probe + REST Fallback

This commit is contained in:
2026-04-29 21:40:25 +03:00
parent 41c332d9cb
commit 7c8895c3a5
17 changed files with 934 additions and 18 deletions

View File

@@ -15,7 +15,7 @@ from src.telegram.handlers.journal_ui import (
build_actions_keyboard,
render_actions,
)
from src.telegram.live.runner import ScreenRegistry, StaticScreen
from src.telegram.live.runner import LiveScreenRunner, ScreenRegistry, StaticScreen
from src.trading.journal.service import JournalService
from src.trading.auto.runner import AutoTradeRunner
@@ -61,6 +61,16 @@ async def _show_journal_page(
if edit_mode:
await target_message.edit_text(text, reply_markup=kb)
LiveScreenRunner.unregister_message(
chat_id=target_message.chat.id,
message_id=target_message.message_id,
)
ScreenRegistry.unregister_message(
chat_id=target_message.chat.id,
message_id=target_message.message_id,
)
ScreenRegistry.register_screen(
StaticScreen(
screen="journal",
@@ -71,6 +81,16 @@ async def _show_journal_page(
)
else:
sent_message = await target_message.answer(text, reply_markup=kb)
LiveScreenRunner.unregister_message(
chat_id=sent_message.chat.id,
message_id=sent_message.message_id,
)
ScreenRegistry.unregister_message(
chat_id=sent_message.chat.id,
message_id=sent_message.message_id,
)
ScreenRegistry.register_screen(
StaticScreen(
screen="journal",
@@ -151,7 +171,7 @@ async def open_journal_from_monitoring(callback: CallbackQuery, state: FSMContex
)
await callback.answer()
@router.callback_query(F.data == "journal:noop")
async def journal_noop(callback: CallbackQuery) -> None:
await callback.answer()

View File

@@ -9,7 +9,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from src.integrations.exchange.exceptions import ExchangeError
from src.integrations.exchange.service import ExchangeService
from src.telegram.live.runner import LiveScreen, LiveScreenRunner
from src.telegram.live.runner import LiveScreen, LiveScreenRunner, ScreenRegistry
from src.telegram.ui.common import mode_line, now_line
from src.telegram.ui.currency_ui import format_usd_amount
from src.telegram.ui.exchange_error import (
@@ -48,9 +48,9 @@ def _build_market_text(
if previous_price is not None:
if ticker_price > previous_price:
price_direction = ""
price_direction = "🔺"
elif ticker_price < previous_price:
price_direction = ""
price_direction = "🔻"
_last_market_prices[name] = ticker_price
_last_market_directions[name] = price_direction
@@ -105,6 +105,15 @@ def _build_market_live_text() -> str:
# зарегистрировать сообщение как live-экран рынка
def _register_market_live_screen(message: Message) -> None:
LiveScreenRunner.unregister_message(
chat_id=message.chat.id,
message_id=message.message_id,
)
ScreenRegistry.unregister_message(
chat_id=message.chat.id,
message_id=message.message_id,
)
LiveScreenRunner.register_screen(
LiveScreen(
screen="market",

View File

@@ -7,6 +7,7 @@ from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, InlineKeyboardMarkup, Message
from aiogram.utils.keyboard import InlineKeyboardBuilder
from src.telegram.live.runner import LiveScreenRunner, ScreenRegistry, StaticScreen
from src.trading.auto.runner import AutoTradeRunner
@@ -31,17 +32,46 @@ def _monitoring_text() -> str:
)
# зарегистрировать сообщение как статичный экран мониторинга
def _register_monitoring_screen(message: Message) -> None:
LiveScreenRunner.unregister_message(
chat_id=message.chat.id,
message_id=message.message_id,
)
ScreenRegistry.unregister_message(
chat_id=message.chat.id,
message_id=message.message_id,
)
ScreenRegistry.register_screen(
StaticScreen(
screen="monitoring",
bot=message.bot,
chat_id=message.chat.id,
message_id=message.message_id,
)
)
# открыть мониторинг из главного меню
@router.message(F.text == "📊 Мониторинг")
async def open_monitoring(message: Message, state: FSMContext) -> None:
await state.clear()
AutoTradeRunner.set_current_screen("monitoring")
await message.answer(
await ScreenRegistry.delete_screen(
screen="monitoring",
bot=message.bot,
chat_id=message.chat.id,
)
sent_message = await message.answer(
_monitoring_text(),
reply_markup=_monitoring_keyboard(),
)
_register_monitoring_screen(sent_message)
# вернуться на экран мониторинга из callback
@router.callback_query(F.data == "monitoring:home")
@@ -57,10 +87,6 @@ async def open_monitoring_callback(callback: CallbackQuery, state: FSMContext) -
_monitoring_text(),
reply_markup=_monitoring_keyboard(),
)
await callback.answer()
# переход к портфелю из мониторинга
# переход к рынку из мониторинга
_register_monitoring_screen(callback.message)
await callback.answer()

View File

@@ -10,7 +10,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from src.integrations.exchange.exceptions import ExchangeError
from src.integrations.exchange.models import BalanceSummary
from src.integrations.exchange.service import ExchangeService
from src.telegram.live.runner import LiveScreen, LiveScreenRunner
from src.telegram.live.runner import LiveScreen, LiveScreenRunner, ScreenRegistry
from src.telegram.ui.common import mode_line, now_line
from src.telegram.ui.currency_ui import format_usd_amount
from src.telegram.ui.currency_ui import (
@@ -197,6 +197,15 @@ def _portfolio_live_markup() -> InlineKeyboardMarkup:
# зарегистрировать сообщение как live-экран портфеля
def _register_portfolio_live_screen(message: Message) -> None:
LiveScreenRunner.unregister_message(
chat_id=message.chat.id,
message_id=message.message_id,
)
ScreenRegistry.unregister_message(
chat_id=message.chat.id,
message_id=message.message_id,
)
LiveScreenRunner.register_screen(
LiveScreen(
screen="portfolio",

View File

@@ -67,6 +67,32 @@ class ScreenRegistry:
]
screens.append(static_screen)
# удалить конкретное сообщение из всех статичных экранов без удаления из Telegram
@classmethod
def unregister_message(
cls,
*,
chat_id: int,
message_id: int,
) -> None:
empty_screens: list[str] = []
for screen, screens in cls._screens.items():
screens[:] = [
item
for item in screens
if not (
item.chat_id == chat_id
and item.message_id == message_id
)
]
if not screens:
empty_screens.append(screen)
for screen in empty_screens:
cls._screens.pop(screen, None)
# удалить старые статичные экраны указанного типа
@classmethod
@@ -118,6 +144,32 @@ class LiveScreenRunner:
]
screens.append(live_screen)
# удалить конкретное сообщение из всех live-экранов без удаления из Telegram
@classmethod
def unregister_message(
cls,
*,
chat_id: int,
message_id: int,
) -> None:
empty_screens: list[str] = []
for screen, screens in cls._screens.items():
screens[:] = [
item
for item in screens
if not (
item.chat_id == chat_id
and item.message_id == message_id
)
]
if not screens:
empty_screens.append(screen)
for screen in empty_screens:
cls._screens.pop(screen, None)
# удалить все live-экраны указанного типа из Telegram
@classmethod