Repository: nonebot/nonebot2

Issue:
Bug: SessionManager has not been initialized
**描述问题：**

正常运行一段时间后出现“SessionManager has not been initialized”的错误

**如何复现？**

1.正常运行一段时间
2.报错

**期望的结果**

不报错

**环境信息：**

 - OS: CentOS8.3.2011
 - Python Version: 3.9
 - Nonebot Version: 2.0.0a14

**协议端信息：**

 - 协议端: MAH
 - 协议端版本: 2.0.0a14

**截图或日志**

__init__.py
```python
from nonebot.adapters.mirai import Bot
from nonebot.plugin import on_startswith
from nonebot.typing import T_State
from nonebot.adapters import Bot, Event
from nonebot.adapters.mirai import Bot, MessageEvent,MessageChain
from nonebot.adapters.mirai.message import MessageSegment
import anyio
from .get import *
setu = on_startswith("来份色图",priority=1)
@setu.handle()
async def MessageReceive(bot: Bot, event: MessageEvent, state: T_State):
    messagedict = str(event.get_message()).split()
    if len(messagedict) == 2:
        await bot.send(event, MessageSegment.plain("正在获取色图，请稍候。"), )
        await bot.send(event, await get.pic(messagedict[1]))
    elif len(messagedict) > 2:
        await bot.send(event, MessageSegment.plain("色图参数错误。"), )
    else:
        await bot.send(event, MessageSegment.plain("正在获取色图，请稍候。"), )
        await bot.send(event, await get.pic())
```

get.py
```python
import time
import httpx
import json
import asyncio
from nonebot.adapters.mirai.message import MessageSegment

async def pic(tag=""):
    if tag=="":
        timeout = httpx.Timeout(60.0, connect=60.0, read=60.0)
        async with httpx.AsyncClient() as client:
            r = await client.get("https://api.lolicon.app/setu/v2?r18=0&size=regular", timeout=timeout)
            await client.aclose()
        s = json.loads(r.text)
        url = s['data'][0]['urls']['regular']
        pid = s['data'][0]['pid']
        title = s['data'][0]['title']
        author = s['data'][0]['author']
        text = MessageSegment.image(url=url), \
               MessageSegment.plain(
                   "作品名：" + title + "\n" + "画师：" + author + "\n" + "Pixiv地址: https://www.pixiv.net/artworks/" + str(
                       pid))
        return text
    else:
        timeout = httpx.Timeout(60.0, connect=60.0, read=60.0)
        async with httpx.AsyncClient() as client:
            r = await client.get("https://api.lolicon.app/setu/v2?r18=0&size=regular&tag="+tag, timeout=timeout)
            await client.aclose()
        s = json.loads(r.text)
        list=s['data']
        if len(list)==0:
            text=MessageSegment.plain("图库中无包含该标签的图片")
            return text
        else:
            url = s['data'][0]['urls']['regular']
            pid = s['data'][0]['pid']
            title = s['data'][0]['title']
            author = s['data'][0]['author']
            text = MessageSegment.image(url=url), \
                   MessageSegment.plain(
                       "作品名：" + title + "\n" + "画师：" + author + "\n" + "Pixiv地址: https://www.pixiv.net/artworks/" + str(
                           pid))
            return text
```

bot.py
```python
import nonebot
from nonebot.adapters.mirai import Bot
nonebot.init()
nonebot.get_driver().register_adapter('mirai', Bot, qq=**********)
nonebot.load_builtin_plugins() 
nonebot.load_plugins("src/plugins")
nonebot.run()
```

.env
```
MIRAI_AUTH_KEY=********
MIRAI_HOST=********
MIRAI_PORT=8080 # MAH的监听端口
PORT=2323
APSCHEDULER_AUTOSTART=true
APSCHEDULER_CONFIG={"apscheduler.timezone": "Asia/Shanghai"}
```
日志
```
08-10 19:30:50 [ERROR] nonebot | Running matcher <Matcher from setu, type=message, priority=1, temp=False> failed.
Traceback (most recent call last):
  File "/root/NoneBot/bot.py", line 7, in <module>
    nonebot.run()
  File "/usr/local/lib/python3.9/site-packages/nonebot/__init__.py", line 277, in run
    get_driver().run(host, port, *args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/nonebot/drivers/fastapi.py", line 214, in run
    uvicorn.run(
  File "/usr/local/lib/python3.9/site-packages/uvicorn/main.py", line 393, in run
    server.run()
  File "/usr/local/lib/python3.9/site-packages/uvicorn/server.py", line 50, in run
    loop.run_until_complete(self.serve(sockets=sockets))
  File "/usr/local/lib/python3.9/site-packages/nonebot/message.py", line 126, in _check_matcher
    await _run_matcher(Matcher, bot, event, state)
  File "/usr/local/lib/python3.9/site-packages/nonebot/message.py", line 154, in _run_matcher
    await matcher.run(bot, event, state)
  File "/usr/local/lib/python3.9/site-packages/nonebot/matcher.py", line 584, in run
    await handler(self, bot, event, self.state)
  File "/usr/local/lib/python3.9/site-packages/nonebot/handler.py", line 81, in __call__
    await self.func(
  File "src/plugins/setu/__init__.py", line 19, in MessageReceive
    await bot.send(event, MessageSegment.plain("正在获取色图，请稍候。"), )
  File "/usr/local/lib/python3.9/site-packages/nonebot/adapters/mirai/bot.py", line 289, in send
    return await self.send_group_message(group=event.sender.group.id,
  File "/usr/local/lib/python3.9/site-packages/nonebot/adapters/mirai/bot.py", line 354, in send_group_message
    return await self.api.post('sendGroupMessage',
  File "/usr/local/lib/python3.9/site-packages/nonebot/adapters/mirai/bot.py", line 168, in api
    assert api is not None, 'SessionManager has not been initialized'
AssertionError: SessionManager has not been initialized
```


Discussion:
喔, 看起来的确是我的问题
是我前面写的`SessionManager`超时检测的逻辑问题

我先提供一个临时解决方案

```python
from nonebot.adapters.mirai.bot import SessionManager
from datetime import timedelta

SessionManager.session_expiry = timedelta.max
```
不知道这样行不行

应该也可以通过把通信方式换成用HTTP上报解决

~~因为下个版本计划换成mah2, 所以可能不会再单独发一个版本修这个问题~~

Resolving patch:
diff --git a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/bot.py b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/bot.py
--- a/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/bot.py
+++ b/packages/nonebot-adapter-mirai/nonebot/adapters/mirai/bot.py
@@ -1,5 +1,5 @@
+import asyncio
 import json
-from datetime import datetime, timedelta
 from io import BytesIO
 from ipaddress import IPv4Address
 from typing import Any, Dict, List, NoReturn, Optional, Tuple, Union
@@ -9,7 +9,9 @@
 
 from nonebot.adapters import Bot as BaseBot
 from nonebot.config import Config
-from nonebot.drivers import Driver, ReverseDriver, HTTPConnection, HTTPResponse, WebSocket, ForwardDriver, WebSocketSetup
+from nonebot.drivers import (Driver, ForwardDriver, HTTPConnection,
+                             HTTPResponse, ReverseDriver, WebSocket,
+                             WebSocketSetup)
 from nonebot.exception import ApiNotAvailable
 from nonebot.typing import overrides
 
@@ -21,8 +23,7 @@
 
 class SessionManager:
     """Bot会话管理器, 提供API主动调用接口"""
-    sessions: Dict[int, Tuple[str, datetime, httpx.AsyncClient]] = {}
-    session_expiry: timedelta = timedelta(minutes=15)
+    sessions: Dict[int, Tuple[str, httpx.AsyncClient]] = {}
 
     def __init__(self, session_key: str, client: httpx.AsyncClient):
         self.session_key, self.client = session_key, client
@@ -126,19 +127,15 @@ async def new(cls, self_id: int, *, host: IPv4Address, port: int,
                                          'qq': self_id
                                      })
         assert response.json()['code'] == 0
-        cls.sessions[self_id] = session_key, datetime.now(), client
+        cls.sessions[self_id] = session_key, client
 
         return cls(session_key, client)
 
     @classmethod
-    def get(cls,
-            self_id: int,
-            check_expire: bool = True) -> Optional["SessionManager"]:
+    def get(cls, self_id: int):
         if self_id not in cls.sessions:
             return None
-        key, time, client = cls.sessions[self_id]
-        if check_expire and (datetime.now() - time > cls.session_expiry):
-            return None
+        key, client = cls.sessions[self_id]
         return cls(key, client)
 
 
@@ -165,6 +162,9 @@ def type(self) -> str:
     def api(self) -> SessionManager:
         """返回该Bot对象的会话管理实例以提供API主动调用"""
         api = SessionManager.get(self_id=int(self.self_id))
+        if api is None:
+            if isinstance(self.request, WebSocket):
+                asyncio.create_task(self.request.close(1000))
         assert api is not None, 'SessionManager has not been initialized'
         return api
 


Test patch: