add global error handle, and update README, add new example for error handle, update msg.py for add new property for SuperChatMsg

This commit is contained in:
Cam 2022-06-21 10:33:40 +08:00
parent 0c1f94b4a8
commit 6b3e896dc6
5 changed files with 132 additions and 1 deletions

View File

@ -156,6 +156,26 @@ async def show():
return list(BLIVER_POOL.keys())
```
## 全局异常处理
全局异常处理分为两个共享级别,分别为类级别和实例级别,在类上注册的异常处理为所有类实例共享,实例级别的异常处理只有实例自身拥有
```python
app = BLiver(510)
@app.catch(ZeroDivisionError)
def err_handler(e, app: BLiver):
print(f"{app.uname} catch ZeroDivisionError", e)
@app.on(Events.DANMU_MSG)
async def danmu_handler(ctx):
1 / 0 # will raise ZeroDivisionError
azi.run()
```
## 项目简介
- blive 文件夹为框架代码
@ -175,6 +195,9 @@ async def show():
- example/with_fastapi.py
与fastapi 配合使用的例子
- example/error_handler.py
错误处理的例子
## TODO
- 更多的消息操作类欢迎各位提pr

View File

@ -1,3 +1,4 @@
from socket import gaierror
import sys
import json
import asyncio
@ -74,6 +75,25 @@ class Processor:
class BLiver:
_global_catches = {}
def catch(self, err_type):
def _err_handler_wrapper(fn):
self.register_error_handler(err_type, fn)
return _err_handler_wrapper
@classmethod
def register_global_error_handler(cls, err_type, fn):
err_handlers = cls._global_catches.get(err_type, [])
err_handlers.append(fn)
cls._global_catches[err_type] = err_handlers
def register_error_handler(self, err_type, fn):
err_handlers = self._catches.get(err_type, [])
err_handlers.append(fn)
self._catches[err_type] = err_handlers
def __init__(self, roomid, uid=0, logger=None, log_level="INFO"):
self.roomid = roomid
self.uid = uid
@ -84,6 +104,7 @@ class BLiver:
self.logger.add(sys.stderr, level=log_level)
else:
self.logger = logger
self._catches = {} # to handle errors
self._ws: ClientWebSocketResponse = None
self.packman = BWS_MsgPackage()
self.scheduler = AsyncIOScheduler(timezone="Asia/ShangHai")
@ -162,6 +183,7 @@ class BLiver:
except (
aiohttp.ClientConnectionError,
asyncio.TimeoutError,
ConnectionError,
ConnectionResetError,
):
self.logger.warning("send heartbeat error, will reconnect ws")
@ -185,6 +207,7 @@ class BLiver:
aiohttp.ClientConnectionError,
asyncio.TimeoutError,
ConnectionError,
ConnectionResetError,
):
self.logger.warning(
"connect failed, will retry {}, current: {}", retries, i + 1
@ -226,6 +249,12 @@ class BLiver:
self.logger.warning("ws conn will reconnect")
await self.connect()
# to handler errors
except tuple(self._catches.keys()) as e:
[eh(e,self) for eh in self._catches.get(type(e), [])]
except tuple(BLiver._global_catches.keys()) as e:
[eh(e,self) for eh in BLiver._global_catches.get(type(e), [])]
async def graceful_close(self):
await self._ws.close()
await self.aio_session.close()

View File

@ -201,6 +201,19 @@ class SuperChatMsg(BaseMsg):
@property
def time(self):
return dict_chain_get(self.body, "data.time")
@property # 头像
def avatar_url(self):
return dict_chain_get(self.body, "data.user_info.face")
@property
def anchor_uname(self):
return dict_chain_get(self.body, "data.medal_info.anchor_uname")
@property # 背景色
def color(self):
return dict_chain_get(self.body, "data.background_bottom_color")
class EntryEffectMsg(BaseMsg):

66
example/error_handler.py Normal file
View File

@ -0,0 +1,66 @@
"""监听多个直播间的例子"""
import asyncio
from blive import BLiver, Events, BLiverCtx
from blive.msg import DanMuMsg
# 多个对象共用的全局异常处理
# 首先定义全局异常处理handler
def global_error_handler(e, app: BLiver):
print(f"{app.uname} 全局异常捕获", e)
# 调用类方法注册异常以及其处理函数,需在实例化之前注册注册后所有BLiver共同拥有该异常处理
BLiver.register_global_error_handler(ZeroDivisionError, global_error_handler)
# 定义弹幕事件handler,为了演示异常处理直接在方法中抛出异常
async def azi_timeout_error(ctx: BLiverCtx):
raise TimeoutError
async def ke_type_error(ctx):
raise TypeError
async def zero_division_error(ctx):
1 / 0
# 两个直播间
ke = BLiver(21716679)
azi = BLiver(7983476)
# 注册handler
ke.register_handler(Events.INTERACT_WORD, zero_division_error)
azi.register_handler(Events.INTERACT_WORD, zero_division_error)
ke.register_handler(Events.DANMU_MSG, ke_type_error)
azi.register_handler(Events.DANMU_MSG, azi_timeout_error)
# 类实例级别的异常处理,实例与实例之间不共享
ke.register_error_handler(
TypeError, lambda e, app: print(f"{app.uname} catch TypeError", e)
)
# 实例级别的异常处理可以用注解方式进行注册
@azi.catch(TimeoutError)
def azi_handler(e, app):
print(f"{app.uname} catch TimeoutError", e)
async def main():
# 以异步task的形式运行
task1 = ke.run_as_task()
task2 = azi.run_as_task()
# await 两个任务
await asyncio.gather(*[task1, task2])
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

View File

@ -9,7 +9,7 @@ from blive.msg import DanMuMsg
async def listen(ctx: BLiverCtx):
danmu = DanMuMsg(ctx.body)
print(
f'\n{danmu.sender.name} ({danmu.sender.medal.medal_name}:{danmu.sender.medal.medal_level}): "{danmu.content}"\n'
f'\n{ctx.bliver.uname}{danmu.sender.name} ({danmu.sender.medal.medal_name}:{danmu.sender.medal.medal_level}): "{danmu.content}"\n'
)