prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Bug: websockets 驱动器连接关闭 code 不存在
  ### 操作系统
  
  Windows
  
  ### Python 版本
  
  3.11.6
  
  ### NoneBot 版本
  
  2.1.2
  
  ### 适配器
  
  nonebot-adapter-kaiheila 0.3.0
  
  ### 协议端
  
  kook API(websockets)
  
  ### 描述问题
  
  在nonebot库的websockets.py模块中，处理WebSocket异常时出现了AttributeError。这个问题发生在尝试处理ConnectionClosed异常的过程中。
  异常信息:
  `AttributeError: 'NoneType' object has no attribute 'code'`
  相关代码:
  ```python
  def catch_closed(func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
      @wraps(func)
      async def decorator(*args: P.args, **kwargs: P.kwargs) -> T:
          try:
              return await func(*args, **kwargs)
          except ConnectionClosed as e:
              if e.rcvd_then_sent:
                  raise WebSocketClosed(e.rcvd.code, e.rcvd.reason)  # type: ignore
              else:
                  raise WebSocketClosed(e.sent.code, e.sent.reason)  # type: ignore
  
      return decorator
  ```
  
  位于:`nonebot/drivers/websockets.py` Line 56
  这个问题是在捕获ConnectionClosed异常时发生的，但e.rcvd或e.sent对象可能为None(在websocket超时这种不是由关闭帧影响的情况下会都不存在)。这导致尝试访问NoneType对象的code属性，从而引发了AttributeError。
  
  ### 复现步骤
  
  1.在环境下加载websockets adapter
  2.在插件的event handler中存在不使用异步的长时间等待情形（在我的例子是等待语言模型的计算）
  
  
  ### 期望的结果
  
  按照上述描述修改此bug
  
  ### 截图或日志
  
  无
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `nonebot/drivers/websockets.py`
  Content:
  ```
  1 """[websockets](https://websockets.readthedocs.io/) 驱动适配
  2 
  3 ```bash
  4 nb driver install websockets
  5 # 或者
  6 pip install nonebot2[websockets]
  7 ```
  8 
  9 :::tip 提示
  10 本驱动仅支持客户端 WebSocket 连接
  11 :::
  12 
  13 FrontMatter:
  14     sidebar_position: 4
  15     description: nonebot.drivers.websockets 模块
  16 """
  17 
  18 import logging
  19 from functools import wraps
  20 from contextlib import asynccontextmanager
  21 from typing_extensions import ParamSpec, override
  22 from typing import TYPE_CHECKING, Union, TypeVar, Callable, Awaitable, AsyncGenerator
  23 
  24 from nonebot.drivers import Request
  25 from nonebot.log import LoguruHandler
  26 from nonebot.exception import WebSocketClosed
  27 from nonebot.drivers.none import Driver as NoneDriver
  28 from nonebot.drivers import WebSocket as BaseWebSocket
  29 from nonebot.drivers import WebSocketClientMixin, combine_driver
  30 
  31 try:
  32     from websockets.exceptions import ConnectionClosed
  33     from websockets.legacy.client import Connect, WebSocketClientProtocol
  34 except ModuleNotFoundError as e:  # pragma: no cover
  35     raise ImportError(
  36         "Please install websockets first to use this driver. "
  37         "Install with pip: `pip install nonebot2[websockets]`"
  38     ) from e
  39 
  40 T = TypeVar("T")
  41 P = ParamSpec("P")
  42 
  43 logger = logging.Logger("websockets.client", "INFO")
  44 logger.addHandler(LoguruHandler())
  45 
  46 
  47 def catch_closed(func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:
  48     @wraps(func)
  49     async def decorator(*args: P.args, **kwargs: P.kwargs) -> T:
  50         try:
  51             return await func(*args, **kwargs)
  52         except ConnectionClosed as e:
  53             if e.rcvd_then_sent:
  54                 raise WebSocketClosed(e.rcvd.code, e.rcvd.reason)  # type: ignore
  55             else:
  56                 raise WebSocketClosed(e.sent.code, e.sent.reason)  # type: ignore
  57 
  58     return decorator
  59 
  60 
  61 class Mixin(WebSocketClientMixin):
  62     """Websockets Mixin"""
  63 
  64     @property
  65     @override
  66     def type(self) -> str:
  67         return "websockets"
  68 
  69     @override
  70     @asynccontextmanager
  71     async def websocket(self, setup: Request) -> AsyncGenerator["WebSocket", None]:
  72         connection = Connect(
  73             str(setup.url),
  74             extra_headers={**setup.headers, **setup.cookies.as_header(setup)},
  75             open_timeout=setup.timeout,
  76         )
  77         async with connection as ws:
  78             yield WebSocket(request=setup, websocket=ws)
  79 
  80 
  81 class WebSocket(BaseWebSocket):
  82     """Websockets WebSocket Wrapper"""
  83 
  84     @override
  85     def __init__(self, *, request: Request, websocket: WebSocketClientProtocol):
  86         super().__init__(request=request)
  87         self.websocket = websocket
  88 
  89     @property
  90     @override
  91     def closed(self) -> bool:
  92         return self.websocket.closed
  93 
  94     @override
  95     async def accept(self):
  96         raise NotImplementedError
  97 
  98     @override
  99     async def close(self, code: int = 1000, reason: str = ""):
  100         await self.websocket.close(code, reason)
  101 
  102     @override
  103     @catch_closed
  104     async def receive(self) -> Union[str, bytes]:
  105         return await self.websocket.recv()
  106 
  107     @override
  108     @catch_closed
  109     async def receive_text(self) -> str:
  110         msg = await self.websocket.recv()
  111         if isinstance(msg, bytes):
  112             raise TypeError("WebSocket received unexpected frame type: bytes")
  113         return msg
  114 
  115     @override
  116     @catch_closed
  117     async def receive_bytes(self) -> bytes:
  118         msg = await self.websocket.recv()
  119         if isinstance(msg, str):
  120             raise TypeError("WebSocket received unexpected frame type: str")
  121         return msg
  122 
  123     @override
  124     async def send_text(self, data: str) -> None:
  125         await self.websocket.send(data)
  126 
  127     @override
  128     async def send_bytes(self, data: bytes) -> None:
  129         await self.websocket.send(data)
  130 
  131 
  132 if TYPE_CHECKING:
  133 
  134     class Driver(Mixin, NoneDriver):
  135         ...
  136 
  137 else:
  138     Driver = combine_driver(NoneDriver, Mixin)
  139     """Websockets Driver"""
  140
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/nonebot/drivers/websockets.py b/nonebot/drivers/websockets.py
  --- a/nonebot/drivers/websockets.py
  +++ b/nonebot/drivers/websockets.py
  @@ -50,10 +50,7 @@
           try:
               return await func(*args, **kwargs)
           except ConnectionClosed as e:
  -            if e.rcvd_then_sent:
  -                raise WebSocketClosed(e.rcvd.code, e.rcvd.reason)  # type: ignore
  -            else:
  -                raise WebSocketClosed(e.sent.code, e.sent.reason)  # type: ignore
  +            raise WebSocketClosed(e.code, e.reason)
   
       return decorator
  
verification_info: {"golden_diff": "diff --git a/nonebot/drivers/websockets.py b/nonebot/drivers/websockets.py\n--- a/nonebot/drivers/websockets.py\n+++ b/nonebot/drivers/websockets.py\n@@ -50,10 +50,7 @@\n         try:\n             return await func(*args, **kwargs)\n         except ConnectionClosed as e:\n-            if e.rcvd_then_sent:\n-                raise WebSocketClosed(e.rcvd.code, e.rcvd.reason)  # type: ignore\n-            else:\n-                raise WebSocketClosed(e.sent.code, e.sent.reason)  # type: ignore\n+            raise WebSocketClosed(e.code, e.reason)\n \n     return decorator\n", "issue": "Bug: websockets \u9a71\u52a8\u5668\u8fde\u63a5\u5173\u95ed code \u4e0d\u5b58\u5728\n### \u64cd\u4f5c\u7cfb\u7edf\r\n\r\nWindows\r\n\r\n### Python \u7248\u672c\r\n\r\n3.11.6\r\n\r\n### NoneBot \u7248\u672c\r\n\r\n2.1.2\r\n\r\n### \u9002\u914d\u5668\r\n\r\nnonebot-adapter-kaiheila 0.3.0\r\n\r\n### \u534f\u8bae\u7aef\r\n\r\nkook API(websockets)\r\n\r\n### \u63cf\u8ff0\u95ee\u9898\r\n\r\n\u5728nonebot\u5e93\u7684websockets.py\u6a21\u5757\u4e2d\uff0c\u5904\u7406WebSocket\u5f02\u5e38\u65f6\u51fa\u73b0\u4e86AttributeError\u3002\u8fd9\u4e2a\u95ee\u9898\u53d1\u751f\u5728\u5c1d\u8bd5\u5904\u7406ConnectionClosed\u5f02\u5e38\u7684\u8fc7\u7a0b\u4e2d\u3002\r\n\u5f02\u5e38\u4fe1\u606f:\r\n`AttributeError: 'NoneType' object has no attribute 'code'`\r\n\u76f8\u5173\u4ee3\u7801:\r\n```python\r\ndef catch_closed(func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:\r\n    @wraps(func)\r\n    async def decorator(*args: P.args, **kwargs: P.kwargs) -> T:\r\n        try:\r\n            return await func(*args, **kwargs)\r\n        except ConnectionClosed as e:\r\n            if e.rcvd_then_sent:\r\n                raise WebSocketClosed(e.rcvd.code, e.rcvd.reason)  # type: ignore\r\n            else:\r\n                raise WebSocketClosed(e.sent.code, e.sent.reason)  # type: ignore\r\n\r\n    return decorator\r\n```\r\n\r\n\u4f4d\u4e8e:`nonebot/drivers/websockets.py` Line 56\r\n\u8fd9\u4e2a\u95ee\u9898\u662f\u5728\u6355\u83b7ConnectionClosed\u5f02\u5e38\u65f6\u53d1\u751f\u7684\uff0c\u4f46e.rcvd\u6216e.sent\u5bf9\u8c61\u53ef\u80fd\u4e3aNone(\u5728websocket\u8d85\u65f6\u8fd9\u79cd\u4e0d\u662f\u7531\u5173\u95ed\u5e27\u5f71\u54cd\u7684\u60c5\u51b5\u4e0b\u4f1a\u90fd\u4e0d\u5b58\u5728)\u3002\u8fd9\u5bfc\u81f4\u5c1d\u8bd5\u8bbf\u95eeNoneType\u5bf9\u8c61\u7684code\u5c5e\u6027\uff0c\u4ece\u800c\u5f15\u53d1\u4e86AttributeError\u3002\r\n\r\n### \u590d\u73b0\u6b65\u9aa4\r\n\r\n1.\u5728\u73af\u5883\u4e0b\u52a0\u8f7dwebsockets adapter\r\n2.\u5728\u63d2\u4ef6\u7684event handler\u4e2d\u5b58\u5728\u4e0d\u4f7f\u7528\u5f02\u6b65\u7684\u957f\u65f6\u95f4\u7b49\u5f85\u60c5\u5f62\uff08\u5728\u6211\u7684\u4f8b\u5b50\u662f\u7b49\u5f85\u8bed\u8a00\u6a21\u578b\u7684\u8ba1\u7b97\uff09\r\n\r\n\r\n### \u671f\u671b\u7684\u7ed3\u679c\r\n\r\n\u6309\u7167\u4e0a\u8ff0\u63cf\u8ff0\u4fee\u6539\u6b64bug\r\n\r\n### \u622a\u56fe\u6216\u65e5\u5fd7\r\n\r\n\u65e0\n", "before_files": [{"content": "\"\"\"[websockets](https://websockets.readthedocs.io/) \u9a71\u52a8\u9002\u914d\n\n```bash\nnb driver install websockets\n# \u6216\u8005\npip install nonebot2[websockets]\n```\n\n:::tip \u63d0\u793a\n\u672c\u9a71\u52a8\u4ec5\u652f\u6301\u5ba2\u6237\u7aef WebSocket \u8fde\u63a5\n:::\n\nFrontMatter:\n    sidebar_position: 4\n    description: nonebot.drivers.websockets \u6a21\u5757\n\"\"\"\n\nimport logging\nfrom functools import wraps\nfrom contextlib import asynccontextmanager\nfrom typing_extensions import ParamSpec, override\nfrom typing import TYPE_CHECKING, Union, TypeVar, Callable, Awaitable, AsyncGenerator\n\nfrom nonebot.drivers import Request\nfrom nonebot.log import LoguruHandler\nfrom nonebot.exception import WebSocketClosed\nfrom nonebot.drivers.none import Driver as NoneDriver\nfrom nonebot.drivers import WebSocket as BaseWebSocket\nfrom nonebot.drivers import WebSocketClientMixin, combine_driver\n\ntry:\n    from websockets.exceptions import ConnectionClosed\n    from websockets.legacy.client import Connect, WebSocketClientProtocol\nexcept ModuleNotFoundError as e:  # pragma: no cover\n    raise ImportError(\n        \"Please install websockets first to use this driver. \"\n        \"Install with pip: `pip install nonebot2[websockets]`\"\n    ) from e\n\nT = TypeVar(\"T\")\nP = ParamSpec(\"P\")\n\nlogger = logging.Logger(\"websockets.client\", \"INFO\")\nlogger.addHandler(LoguruHandler())\n\n\ndef catch_closed(func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:\n    @wraps(func)\n    async def decorator(*args: P.args, **kwargs: P.kwargs) -> T:\n        try:\n            return await func(*args, **kwargs)\n        except ConnectionClosed as e:\n            if e.rcvd_then_sent:\n                raise WebSocketClosed(e.rcvd.code, e.rcvd.reason)  # type: ignore\n            else:\n                raise WebSocketClosed(e.sent.code, e.sent.reason)  # type: ignore\n\n    return decorator\n\n\nclass Mixin(WebSocketClientMixin):\n    \"\"\"Websockets Mixin\"\"\"\n\n    @property\n    @override\n    def type(self) -> str:\n        return \"websockets\"\n\n    @override\n    @asynccontextmanager\n    async def websocket(self, setup: Request) -> AsyncGenerator[\"WebSocket\", None]:\n        connection = Connect(\n            str(setup.url),\n            extra_headers={**setup.headers, **setup.cookies.as_header(setup)},\n            open_timeout=setup.timeout,\n        )\n        async with connection as ws:\n            yield WebSocket(request=setup, websocket=ws)\n\n\nclass WebSocket(BaseWebSocket):\n    \"\"\"Websockets WebSocket Wrapper\"\"\"\n\n    @override\n    def __init__(self, *, request: Request, websocket: WebSocketClientProtocol):\n        super().__init__(request=request)\n        self.websocket = websocket\n\n    @property\n    @override\n    def closed(self) -> bool:\n        return self.websocket.closed\n\n    @override\n    async def accept(self):\n        raise NotImplementedError\n\n    @override\n    async def close(self, code: int = 1000, reason: str = \"\"):\n        await self.websocket.close(code, reason)\n\n    @override\n    @catch_closed\n    async def receive(self) -> Union[str, bytes]:\n        return await self.websocket.recv()\n\n    @override\n    @catch_closed\n    async def receive_text(self) -> str:\n        msg = await self.websocket.recv()\n        if isinstance(msg, bytes):\n            raise TypeError(\"WebSocket received unexpected frame type: bytes\")\n        return msg\n\n    @override\n    @catch_closed\n    async def receive_bytes(self) -> bytes:\n        msg = await self.websocket.recv()\n        if isinstance(msg, str):\n            raise TypeError(\"WebSocket received unexpected frame type: str\")\n        return msg\n\n    @override\n    async def send_text(self, data: str) -> None:\n        await self.websocket.send(data)\n\n    @override\n    async def send_bytes(self, data: bytes) -> None:\n        await self.websocket.send(data)\n\n\nif TYPE_CHECKING:\n\n    class Driver(Mixin, NoneDriver):\n        ...\n\nelse:\n    Driver = combine_driver(NoneDriver, Mixin)\n    \"\"\"Websockets Driver\"\"\"\n", "path": "nonebot/drivers/websockets.py"}], "after_files": [{"content": "\"\"\"[websockets](https://websockets.readthedocs.io/) \u9a71\u52a8\u9002\u914d\n\n```bash\nnb driver install websockets\n# \u6216\u8005\npip install nonebot2[websockets]\n```\n\n:::tip \u63d0\u793a\n\u672c\u9a71\u52a8\u4ec5\u652f\u6301\u5ba2\u6237\u7aef WebSocket \u8fde\u63a5\n:::\n\nFrontMatter:\n    sidebar_position: 4\n    description: nonebot.drivers.websockets \u6a21\u5757\n\"\"\"\n\nimport logging\nfrom functools import wraps\nfrom contextlib import asynccontextmanager\nfrom typing_extensions import ParamSpec, override\nfrom typing import TYPE_CHECKING, Union, TypeVar, Callable, Awaitable, AsyncGenerator\n\nfrom nonebot.drivers import Request\nfrom nonebot.log import LoguruHandler\nfrom nonebot.exception import WebSocketClosed\nfrom nonebot.drivers.none import Driver as NoneDriver\nfrom nonebot.drivers import WebSocket as BaseWebSocket\nfrom nonebot.drivers import WebSocketClientMixin, combine_driver\n\ntry:\n    from websockets.exceptions import ConnectionClosed\n    from websockets.legacy.client import Connect, WebSocketClientProtocol\nexcept ModuleNotFoundError as e:  # pragma: no cover\n    raise ImportError(\n        \"Please install websockets first to use this driver. \"\n        \"Install with pip: `pip install nonebot2[websockets]`\"\n    ) from e\n\nT = TypeVar(\"T\")\nP = ParamSpec(\"P\")\n\nlogger = logging.Logger(\"websockets.client\", \"INFO\")\nlogger.addHandler(LoguruHandler())\n\n\ndef catch_closed(func: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]]:\n    @wraps(func)\n    async def decorator(*args: P.args, **kwargs: P.kwargs) -> T:\n        try:\n            return await func(*args, **kwargs)\n        except ConnectionClosed as e:\n            raise WebSocketClosed(e.code, e.reason)\n\n    return decorator\n\n\nclass Mixin(WebSocketClientMixin):\n    \"\"\"Websockets Mixin\"\"\"\n\n    @property\n    @override\n    def type(self) -> str:\n        return \"websockets\"\n\n    @override\n    @asynccontextmanager\n    async def websocket(self, setup: Request) -> AsyncGenerator[\"WebSocket\", None]:\n        connection = Connect(\n            str(setup.url),\n            extra_headers={**setup.headers, **setup.cookies.as_header(setup)},\n            open_timeout=setup.timeout,\n        )\n        async with connection as ws:\n            yield WebSocket(request=setup, websocket=ws)\n\n\nclass WebSocket(BaseWebSocket):\n    \"\"\"Websockets WebSocket Wrapper\"\"\"\n\n    @override\n    def __init__(self, *, request: Request, websocket: WebSocketClientProtocol):\n        super().__init__(request=request)\n        self.websocket = websocket\n\n    @property\n    @override\n    def closed(self) -> bool:\n        return self.websocket.closed\n\n    @override\n    async def accept(self):\n        raise NotImplementedError\n\n    @override\n    async def close(self, code: int = 1000, reason: str = \"\"):\n        await self.websocket.close(code, reason)\n\n    @override\n    @catch_closed\n    async def receive(self) -> Union[str, bytes]:\n        return await self.websocket.recv()\n\n    @override\n    @catch_closed\n    async def receive_text(self) -> str:\n        msg = await self.websocket.recv()\n        if isinstance(msg, bytes):\n            raise TypeError(\"WebSocket received unexpected frame type: bytes\")\n        return msg\n\n    @override\n    @catch_closed\n    async def receive_bytes(self) -> bytes:\n        msg = await self.websocket.recv()\n        if isinstance(msg, str):\n            raise TypeError(\"WebSocket received unexpected frame type: str\")\n        return msg\n\n    @override\n    async def send_text(self, data: str) -> None:\n        await self.websocket.send(data)\n\n    @override\n    async def send_bytes(self, data: bytes) -> None:\n        await self.websocket.send(data)\n\n\nif TYPE_CHECKING:\n\n    class Driver(Mixin, NoneDriver):\n        ...\n\nelse:\n    Driver = combine_driver(NoneDriver, Mixin)\n    \"\"\"Websockets Driver\"\"\"\n", "path": "nonebot/drivers/websockets.py"}]}