# -*- coding: utf-8 -*-
import logging
#Python中的logging模块,是用于实现日志的功能。
#此日志的功能,简单的说,可以控制需要输出的信息,输出(显示)到哪里;相关要显示的信息,有很多种等级,比如info,warning,error等
#
"""最常见的应用是:
把info,warning,error同时输出到cmd窗口(显示)和(写入)log文件中;
其中info表示告诉用户,这个是普通的信息;
warning和error分别提醒用户,有些警告,甚至是错误信息,需要用户注意;
把debug类信息,只输出(写入)到log文件中;
"""
from typing import *
from . import client as client_
from . import models
__all__ = (
'HandlerInterface',
'BaseHandler',
)
"""以单下划线开头(_foo)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用“from xxx import *”而导入;以双下划线开头的(__foo)代表类的私有成员;以双下划线开头和结尾的(__foo__)代表python里特殊方法专用的标识,如 __init__()代表类的构造函数。"""
#
#Python模块__all__变量
#该变量的值是一个列表,存储的是当前模块中一些成员(变量、函数或者类)的名称。通过在模块文件中设置 __all__ 变量,当其它文件以“from 模块名 import *”的形式导入该模块时,该文件中只能使用 __all__ 列表中指定的成员。
#也就是说,只有以“from 模块名 import *”形式导入的模块,当该模块设有 __all__ 变量时,只能导入该变量指定的成员,未指定的成员是无法导入的。
logger = logging.getLogger('blivedm') #返回一个logger对象
# 常见可忽略的cmd
IGNORED_CMDS = (
'COMBO_SEND',
'ENTRY_EFFECT',
'HOT_RANK_CHANGED',
'HOT_RANK_CHANGED_V2',
'INTERACT_WORD',
'LIVE',
'LIVE_INTERACTIVE_GAME',
'NOTICE_MSG',
'ONLINE_RANK_COUNT',
'ONLINE_RANK_TOP3',
'ONLINE_RANK_V2',
'PK_BATTLE_END',
'PK_BATTLE_FINAL_PROCESS',
'PK_BATTLE_PROCESS',
'PK_BATTLE_PROCESS_NEW',
'PK_BATTLE_SETTLE',
'PK_BATTLE_SETTLE_USER',
'PK_BATTLE_SETTLE_V2',
'PREPARING',
'ROOM_REAL_TIME_MESSAGE_UPDATE',
'STOP_LIVE_ROOM_LIST',
'SUPER_CHAT_MESSAGE_JPN',
'WIDGET_BANNER',
)
# 已打日志的未知cmd
logged_unknown_cmds = set()
class HandlerInterface:
"""
直播消息处理器接口
"""
async def handle(self, client: client_.BLiveClient, command: dict):
raise NotImplementedError #raise 手动抛出异常
"""
raise 语句的基本语法格式为:
raise [exceptionName [(reason)]]
其中,用 [] 括起来的为可选参数,其作用是指定抛出的异常名称,以及异常信息的相关描述。如果可选参数全部省略,则 raise 会把当前错误原样抛出;如果仅省略 (reason),则在抛出异常时,将不附带任何的异常描述信息。
也就是说,raise 语句有如下三种常用的用法:
raise:单独一个 raise。该语句引发当前上下文中捕获的异常(比如在 except 块中),或默认引发 RuntimeError 异常。
raise 异常类名称:raise 后带一个异常类名称,表示引发执行类型的异常。
raise 异常类名称(描述信息):在引发指定类型的异常的同时,附带异常的描述信息。
raise 语句引发的异常通常用 try except(else finally)异常处理结构来捕获并进行处理。
try:
a = input("输入一个数:")
#判断用户输入的是否为数字
if(not a.isdigit()):
raise ValueError("a 必须是数字")
except ValueError as e:
print("引发异常:",repr(e))
"""
'''
raise NotImplementedError
在面向对象编程中,可以先预留一个方法接口不实现,在其子类中实现。
如果要求其子类一定要实现,不实现的时候会导致问题,那么采用raise的方式就很好。
程序识别到了这个方法并没有在子类中实现却被调用了。且只有这个子类的实例化对象调用相应的方法的时候才会报错。
只要相应的方法接口进行了实现,在执行的时候未实施的错误便不会报出。
在此例中对应的是handle方法
'''
class BaseHandler(HandlerInterface):
"""
一个简单的消息处理器实现,带消息分发和消息类型转换。继承并重写_on_xxx方法即可实现自己的处理器
"""
def __heartbeat_callback(self, client: client_.BLiveClient, command: dict):
return self._on_heartbeat(client, models.HeartbeatMessage.from_command(command['data']))
def __danmu_msg_callback(self, client: client_.BLiveClient, command: dict):
return self._on_danmaku(client, models.DanmakuMessage.from_command(command['info']))
def __send_gift_callback(self, client: client_.BLiveClient, command: dict):
return self._on_gift(client, models.GiftMessage.from_command(command['data']))
def __guard_buy_callback(self, client: client_.BLiveClient, command: dict):
return self._on_buy_guard(client, models.GuardBuyMessage.from_command(command['data']))
def __super_chat_message_callback(self, client: client_.BLiveClient, command: dict):
return self._on_super_chat(client, models.SuperChatMessage.from_command(command['data']))
def __super_chat_message_delete_callback(self, client: client_.BLiveClient, command: dict):
return self._on_super_chat_delete(client, models.SuperChatDeleteMessage.from_command(command['data']))
#在定义函数时,变量默认值用=,变量默认类型用:
"""
Typing包中的Optional类
意思是可选类型,作用几乎和带默认值的参数等价,不同的是使用Optional会告诉你的IDE或者框架:这个参数除了给定的默认值外还可以是None,而且使用有些静态检查工具如mypy时,对 a: int =None这样类似的声明可能会提示报错,但使用a :Optional[int] = None不会。
Typing包中的Callable
是一个可调用对象类型,查看是否可调用
第一个用法:isinstance(对象, Callable) # 返回True或False
这里是第二个用法Callable 作为函数参数
Callable 作为函数参数使用,其实只是做一个类型检查的作用,检查传入的参数值 get_func 是否为可调用对象
第三个用法Callable 作为函数返回值使用,其实只是做一个类型检查的作用,看看返回值是否为可调用对象
#def XXX -> 数据类型
#标注返回值的数据类型
#def f(ham: str, eggs: str = 'eggs') -> str:
def get_name_return() -> Callable[[str], None]:
return print_name
# 等价写法,相当于直接返回一个函数对象
def get_name_test():
return print_name
"""
"""这里应该是定义了一个callback的数据结构(字典),然后定义了字典的Optional默认参数,最后检查其可调用性"""
# cmd -> 处理回调
_CMD_CALLBACK_DICT: Dict[
str,
Optional[Callable[
['BaseHandler', client_.BLiveClient, dict],
Awaitable
]]#
] = {
# 收到心跳包,这是blivedm自造的消息,原本的心跳包格式不一样
'_HEARTBEAT': __heartbeat_callback,
# 收到弹幕
# go-common\app\service\live\live-dm\service\v1\send.go
'DANMU_MSG': __danmu_msg_callback,
# 有人送礼
'SEND_GIFT': __send_gift_callback,
# 有人上舰
'GUARD_BUY': __guard_buy_callback,
# 醒目留言
'SUPER_CHAT_MESSAGE': __super_chat_message_callback,
# 删除醒目留言
'SUPER_CHAT_MESSAGE_DELETE': __super_chat_message_delete_callback,
}#大括号括出的是字典,中括号括出的是列表
# 忽略其他常见cmd
for cmd in IGNORED_CMDS:
_CMD_CALLBACK_DICT[cmd] = None
del cmd
async def handle(self, client: client_.BLiveClient, command: dict):
cmd = command.get('cmd', '')
pos = cmd.find(':') # 2019-5-29 B站弹幕升级新增了参数
if pos != -1:
cmd = cmd[:pos]
if cmd not in self._CMD_CALLBACK_DICT:
# 只有第一次遇到未知cmd时打日志
if cmd not in logged_unknown_cmds:
logger.warning('room=%d unknown cmd=%s, command=%s', client.room_id, cmd, command)
logged_unknown_cmds.add(cmd)
return
callback = self._CMD_CALLBACK_DICT[cmd]
if callback is not None:
await callback(self, client, command)
async def _on_heartbeat(self, client: client_.BLiveClient, message: models.HeartbeatMessage):
"""
收到心跳包(人气值)
"""
async def _on_danmaku(self, client: client_.BLiveClient, message: models.DanmakuMessage):
"""
收到弹幕
"""
async def _on_gift(self, client: client_.BLiveClient, message: models.GiftMessage):
"""
收到礼物
"""
async def _on_buy_guard(self, client: client_.BLiveClient, message: models.GuardBuyMessage):
"""
有人上舰
"""
async def _on_super_chat(self, client: client_.BLiveClient, message: models.SuperChatMessage):
"""
醒目留言
"""
async def _on_super_chat_delete(self, client: client_.BLiveClient, message: models.SuperChatDeleteMessage):
"""
删除醒目留言
"""
参考链接
Python - typing 模块 —— Callable
Python:Optional和带默认值的参数
ython编程中NotImplementedError的使用方法
[Python模块学习——logging](https://www.cnblogs.com/captain_jack/archive/2011/01/21/1941453.html)
[Python日志库logging总结-可能是目前为止将logging库总结的最好的一篇文章](https://cloud.tencent.com/developer/article/1354396#:~:text=2%E3%80%81logging%20%E6%B5%81%E7%A8%8B%201%20%E5%88%A4%E6%96%AD%20Logger%20%E5%AF%B9%E8%B1%A1%E5%AF%B9%E4%BA%8E%E8%AE%BE%E7%BD%AE%E7%9A%84%E7%BA%A7%E5%88%AB%E6%98%AF%E5%90%A6%E5%8F%AF%E7%94%A8%EF%BC%8C%E5%A6%82%E6%9E%9C%E5%8F%AF%E7%94%A8%EF%BC%8C%E5%88%99%E5%BE%80%E4%B8%8B%E6%89%A7%E8%A1%8C%EF%BC%8C%E5%90%A6%E5%88%99%EF%BC%8C%E6%B5%81%E7%A8%8B%E7%BB%93%E6%9D%9F%E3%80%82%202%20%E5%88%9B%E5%BB%BA,%E5%A6%82%E6%9E%9C%E4%BC%A0%E5%85%A5%E7%9A%84%20Handler%20%E5%A4%A7%E4%BA%8E%20Logger%20%E4%B8%AD%E8%AE%BE%E7%BD%AE%E7%9A%84%E7%BA%A7%E5%88%AB%EF%BC%8C%E4%B9%9F%E5%8D%B3%20Handler%20%E6%9C%89%E6%95%88%EF%BC%8C%E5%88%99%E5%BE%80%E4%B8%8B%E6%89%A7%E8%A1%8C%EF%BC%8C%E5%90%A6%E5%88%99%EF%BC%8C%E6%B5%81%E7%A8%8B%E7%BB%93%E6%9D%9F%E3%80%82%20%E6%9B%B4%E5%A4%9A%E9%A1%B9%E7%9B%AE)
[Python模块和包](http://c.biancheng.net/python/module_package/)
评论 (0)