From 8e8b0b3bb90bf209cd4146280ff9b4df641abab3 Mon Sep 17 00:00:00 2001 From: firedotguy Date: Tue, 10 Feb 2026 17:53:48 +0300 Subject: [PATCH] chore: stylize sse code; fix: add sseclient-py to requirements --- itd/client.py | 49 +++++++++++++++++++------------------ itd/models/event.py | 20 ++------------- itd/routes/notifications.py | 2 +- pyproject.toml | 2 +- setup.py | 2 +- 5 files changed, 30 insertions(+), 45 deletions(-) diff --git a/itd/client.py b/itd/client.py index f238984..62f19bb 100644 --- a/itd/client.py +++ b/itd/client.py @@ -2,8 +2,8 @@ from uuid import UUID from _io import BufferedReader from typing import cast, Iterator from datetime import datetime -import json -import time +from json import JSONDecodeError, loads +from time import sleep from requests.exceptions import ConnectionError, HTTPError from sseclient import SSEClient @@ -1105,59 +1105,59 @@ class Client: @refresh_on_error def stream_notifications(self) -> Iterator[StreamConnect | StreamNotification]: """Слушать SSE поток уведомлений - + Yields: StreamConnect | StreamNotification: События подключения или уведомления - + Example: ```python from itd import ITDClient - + client = ITDClient(cookies='refresh_token=...') - + # Запуск прослушивания for event in client.stream_notifications(): if isinstance(event, StreamConnect): print(f'Подключено: {event.user_id}') else: print(f'Уведомление: {event.type} от {event.actor.username}') - + # Остановка из другого потока или обработчика # client.stop_stream() ``` """ self._stream_active = True - + while self._stream_active: try: response = stream_notifications(self.token) response.raise_for_status() - + client = SSEClient(response) - + for event in client.events(): if not self._stream_active: response.close() return - + try: if not event.data or event.data.strip() == '': continue - - data = json.loads(event.data) - + + data = loads(event.data) + if 'userId' in data and 'timestamp' in data and 'type' not in data: yield StreamConnect.model_validate(data) else: yield StreamNotification.model_validate(data) - - except json.JSONDecodeError: + + except JSONDecodeError: print(f'Не удалось распарсить сообщение: {event.data}') continue except Exception as e: print(f'Ошибка обработки события: {e}') continue - + except Unauthorized: if self.cookies and self._stream_active: print('Токен истек, обновляем...') @@ -1169,27 +1169,27 @@ class Client: if not self._stream_active: return print(f'Ошибка соединения: {e}, переподключение через 5 секунд...') - time.sleep(5) + sleep(5) continue - + def stop_stream(self): """Остановить прослушивание SSE потока - + Example: ```python import threading from itd import ITDClient - + client = ITDClient(cookies='refresh_token=...') - + # Запуск в отдельном потоке def listen(): for event in client.stream_notifications(): print(event) - + thread = threading.Thread(target=listen) thread.start() - + # Остановка через 10 секунд import time time.sleep(10) @@ -1197,4 +1197,5 @@ class Client: thread.join() ``` """ + print('stop event') self._stream_active = False \ No newline at end of file diff --git a/itd/models/event.py b/itd/models/event.py index 775bd8c..fa4ef2d 100644 --- a/itd/models/event.py +++ b/itd/models/event.py @@ -1,11 +1,8 @@ from uuid import UUID -from datetime import datetime -from typing import Literal from pydantic import BaseModel, Field -from itd.enums import NotificationType, NotificationTargetType -from itd.models.user import UserNotification +from itd.models.notification import Notification class StreamConnect(BaseModel): @@ -14,20 +11,7 @@ class StreamConnect(BaseModel): timestamp: int -class StreamNotification(BaseModel): +class StreamNotification(Notification): """Уведомление из SSE потока""" - id: UUID - type: NotificationType - - target_type: NotificationTargetType | None = Field(None, alias='targetType') - target_id: UUID | None = Field(None, alias='targetId') - - preview: str | None = None - read_at: datetime | None = Field(None, alias='readAt') - created_at: datetime = Field(alias='createdAt') - user_id: UUID = Field(alias='userId') - actor: UserNotification - - read: bool = False sound: bool = True diff --git a/itd/routes/notifications.py b/itd/routes/notifications.py index a13111d..aa91325 100644 --- a/itd/routes/notifications.py +++ b/itd/routes/notifications.py @@ -16,7 +16,7 @@ def get_unread_notifications_count(token: str): def stream_notifications(token: str): """Получить SSE поток уведомлений - + Returns: Response: Streaming response для SSE """ diff --git a/pyproject.toml b/pyproject.toml index d5390c8..49e7d79 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,6 @@ authors = [ ] license = "MIT" dependencies = [ - "requests", "pydantic" + "requests", "pydantic", "sseclient-py" ] requires-python = ">=3.9" diff --git a/setup.py b/setup.py index 8f007b7..6b0f401 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( version='1.1.0', packages=find_packages(), install_requires=[ - 'requests', 'pydantic' + 'requests', 'pydantic', 'sseclient-py' ], python_requires=">=3.9" )