blivedm 项目阅读 之 handlers.py

连祈
2022-12-06 / 0 评论 / 5 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2022年12月06日,已超过865天没有更新,若内容或图片失效,请留言反馈。

Logging 官方文档

# -*- 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/) 

Python 中下划线的 5 种含义

0

评论 (0)

取消