Assistant: 
User: User: Привет. Я тут делаю связь микросервисов через вебсокет. Вот написал класс для главного сервиса. Он получает все микросервисы из базы данных и должен каждый мониторить. Он должен выставлять статусы для микросервисов, работают они в данный момент или нет. Если нет то переподключаться.

Посмотри на мой код, и сделай его более красивым и качественным. Комментарии в функциях кстати можешь тоже как то красиво написать

```
import asyncio
import traceback
import websockets
from asyncio import Task
from websockets.client import WebSocketClientProtocol
from dataclasses import dataclass, field
from typing import Dict, List, Optional

from loguru import logger
from pydantic import BaseModel

from ..database.crud import CommonCRUD
from ..database.models import Microservice


@dataclass
class MicroserviceInfo:
    id: int
    name: str
    description: str
    ws: WebSocketClientProtocol
    manage_task: Task
    status: bool = field(default=False)


class BotResponse(BaseModel):
    name: str
    description: str


class BotsManager:
    def __init__(self, db_crud: CommonCRUD, recheck_sec: int = 10) -> None:
        self.db_crud: CommonCRUD = db_crud
        self.recheck_sec: int = recheck_sec
        self.microservices: Dict[int, MicroserviceInfo] = {}
        self.control_task: Optional[Task] = None

    async def _microservice_ws(self, microservice_id: int, ws: WebSocketClientProtocol) -> None:
        """
        Функция обработки сообщений от микросервиса
        """
        try:
            async for msg in ws:
                try:
                    if microservice_id in self.microservices:
                        self.microservices[microservice_id].status = True
                except websockets.ConnectionClosed:
                    if microservice_id in self.microservices:
                        self.microservices[microservice_id].status = False
                    break
        except Exception:
            logger.error(f"An error occurred while managing microservice {microservice_id}: {traceback.format_exc()}")
        finally:
            await ws.close()

    async def _bots_control_task(self) -> None:
        """
        Основная задача по добавлению отслеживаний микросервисов.
        Здесь мы подтягиваем все микросервисы из базы данных и открываем сессию общения с каждым.
        """
        while True:
            try:
                microservices: List[Microservice] = await self.db_crud.get_microservices()

                for microservice in microservices:
                    if microservice.id in self.microservices and self.microservices[microservice.id].status:
                        continue
                    
                    if microservice.id in self.microservices:
                        if not self.microservices[microservice.id].manage_task.cancelled():
                            self.microservices[microservice.id].manage_task.cancel()

                        if not self.microservices[microservice.id].ws.closed:
                            await self.microservices[microservice.id].ws.close()

                    try:
                        ws: WebSocketClientProtocol = await websockets.connect(microservice.url)
                        task: Task = asyncio.create_task(self._microservice_ws(microservice.id, ws))
                    except Exception as e:
                        logger.error(f"Failed to connect to \"{microservice.name}\" ({microservice.url}): {e}")
                        continue

                    if microservice.id in self.microservices:
                        self.microservices[microservice.id].ws = ws
                        self.microservices[microservice.id].manage_task = task
                    else:
                        self.microservices[microservice.id] = MicroserviceInfo(
                            id=microservice.id,
                            name=microservice.name,
                            ws=ws,
                            manage_task=task,
                            description=microservice.description
                        )  

                await asyncio.sleep(self.recheck_sec)
            except asyncio.CancelledError:
                logger.info("Bots control task was cancelled.")
                break
            except Exception:
                logger.error(f"An error occurred in bots control task: {traceback.format_exc()}")

    async def manage_microservices(self) -> None:
        """
        Точка входа. 
        Запуск задач для отслеживания и общения с микросервисами
        """
        if self.control_task and not self.control_task.cancelled():
            self.control_task.cancel()
        
        self.control_task = asyncio.create_task(self._bots_control_task())
        logger.debug("Началось отслеживание микросервисов.")

    async def close_connections(self) -> None:
        """
        Завершение задач. Полная остановка.
        """
        logger.debug("Остановка отслеживания микросервисов.")
        if self.control_task and not self.control_task.cancelled():
            self.control_task.cancel()

        for m in self.microservices.values():
            if m.manage_task and not m.manage_task.cancelled():
                m.manage_task.cancel()
                await m.ws.close()
```
Assistant: В вашем коде уже проделана хорошая работа, но есть несколько способов сделать его более читабельным и надежным. Вот некоторые предложения по улучшению:

1. **Логирование**: Используйте более подробные сообщения в логах, если это возможно.
2. **Отдельный метод для переподключения**: Вынос логики переподключения в отдельный метод.
3. **Обработка ошибок**: Используйте более конкретные исключения.
4. **Улучшение структуры**: Немного упрощены логические ветвления.

Вот переписанный код с учётом этих пунктов:

```python
import asyncio
import traceback
import websockets
from asyncio import Task
from websockets.exceptions import ConnectionClosed, WebSocketException
from websockets.client import WebSocketClientProtocol
from dataclasses import dataclass, field
from typing import Dict, Optional, List

from loguru import logger
from pydantic import BaseModel

from ..database.crud import CommonCRUD
from ..database.models import Microservice


@dataclass
class MicroserviceInfo:
    id: int
    name: str
    description: str
    ws: Optional[WebSocketClientProtocol] = None
    manage_task: Optional[Task] = None
    status: bool = field(default=False)


class BotResponse(BaseModel):
    name: str
    description: str


class BotsManager:
    def __init__(self, db_crud: CommonCRUD, recheck_sec: int = 10) -> None:
        self.db_crud: CommonCRUD = db_crud
        self.recheck_sec: int = recheck_sec
        self.microservices: Dict[int, MicroserviceInfo] = {}
        self.control_task: Optional[Task] = None

    async def _microservice_ws(self, microservice_id: int, ws: WebSocketClientProtocol) -> None:
        """Receive and handle messages from a microservice."""
        try:
            async for _ in ws:
                if microservice_id in self.microservices:
                    self.microservices[microservice_id].status = True
        except ConnectionClosed:
            self._set_microservice_status(microservice_id, False)
        except Exception as e:
            logger.error(f"Error in microservice {microservice_id}: {str(e)} | {traceback.format_exc()}")
        finally:
            await ws.close()

    def _set_microservice_status(self, microservice_id: int, status: bool) -> None:
        """Set the status of a given microservice."""
        if microservice_id in self.microservices:
            self.microservices[microservice_id].status = status

    async def _reconnect_microservice(self, microservice: Microservice) -> bool:
        """Reconnect to a microservice by URL."""
        try:
            ws = await websockets.connect(microservice.url)
            task = asyncio.create_task(self._microservice_ws(microservice.id, ws))
            self.microservices[microservice.id] = MicroserviceInfo(
                id=microservice.id,
                name=microservice.name,
                description=microservice.description,
                ws=ws,
                manage_task=task
            )
            return True
        except WebSocketException as e:
            logger.error(f"Failed to connect to \"{microservice.name}\" ({microservice.url}): {str(e)}")
            return False

    async def _bots_control_task(self) -> None:
        """Manage connections and reconnections to all microservices."""
        while True:
            try:
                current_microservices = await self.db_crud.get_microservices()
                for microservice in current_microservices:
                    info = self.microservices.get(microservice.id)

                    if info and info.status:
                        continue
                    
                    if info and info.ws:
                        await info.ws.close()
                    
                    await self._reconnect_microservice(microservice)

                await asyncio.sleep(self.recheck_sec)
            except asyncio.CancelledError:
                logger.info("Bots control task was cancelled.")
                break
            except Exception as e:
                logger.error(f"Error in bots control task: {str(e)} | {traceback.format_exc()}")

    async def manage_microservices(self) -> None:
        """Start managing microservice connections."""
        if self.control_task and not self.control_task.cancelled():
            self.control_task.cancel()

        self.control_task = asyncio.create_task(self._bots_control_task())
        logger.debug("Microservices monitoring started.")

    async def close_connections(self) -> None:
        """Shutdown all connections and tasks."""
        logger.debug("Stopping microservice monitoring.")
        if self.control_task and not self.control_task.cancelled():
            self.control_task.cancel()

        for m in self.microservices.values():
            if m.manage_task and not m.manage_task.cancelled():
                m.manage_task.cancel()
            if m.ws:
                await m.ws.close()
```

**Объяснение изменений:**

- **Именование методов**: Методы с `manage_`, `close_` и т.д. дают понять, что именно планируется выполнить.
- **Отдельный метод _reconnect_microservice**: Выделение логики переподключения в отдельный метод упрощает понимание кода.
- **Улучшение логирования**: Прямое получение строки исключения для простоты восприятия.
- **Обработка ошибок**: В логировании ошибок и исключений улучшена читаемость за счет использования `str(e)`.
User: Отлично! Ты довольно хорошо всё оформил. Логику упростил. 
А можешь мне еще вынести общение по вебсокету с микросервисом в отдельный класс. Чтобы я мог там уже отдельно от этого класса прописывать логику что мне там приходит в сообщениях от микросервиса и обрабатывать.
Assistant: Конечно! Вынести общение по вебсокету в отдельный класс — отличная идея. Это поможет сделать код более организованным и удобным для дальнейшей работы. 

Вот пример, как можно это реализовать:

```python
import websocket

class WebSocketClient:
    def __init__(self, url):
        self.url = url
        self.ws = None

    def on_message(self, ws, message):
        # Здесь вы можете обрабатывать сообщения от микросервиса
        print(f"Получено сообщение: {message}")
        self.handle_message(message)

    def on_error(self, ws, error):
        print(f"Ошибка: {error}")

    def on_close(self, ws):
        print("Соединение закрыто")

    def on_open(self, ws):
        print("Соединение открыто")

    def handle_message(self, message):
        # Ваша логика обработки сообщений
        pass

    def run(self):
        self.ws = websocket.WebSocketApp(self.url,
                                          on_message=self.on_message,
                                          on_error=self.on_error,
                                          on_close=self.on_close)
        self.ws.on_open = self.on_open
        self.ws.run_forever()

# Пример использования
if __name__ == "__main__":
    client = WebSocketClient("ws://example.com/socket")
    client.run()
```

В этом примере класс `WebSocketClient` отвечает за управление соединением и обработку сообщений. Вы можете добавить свою логику в метод `handle_message`, чтобы обрабатывать входящие сообщения так, как вам нужно. Если у вас есть дополнительные вопросы или нужна помощь с реализацией, не стесняйтесь спрашивать!
User: а как его интегрировать с тем классом
Assistant:
Assistant: