GraiaProject / Application

一个设计精巧, 协议实现完备的, 基于 mirai-api-http 的即时聊天软件自动化框架.
https://graia-document.vercel.app/
GNU Affero General Public License v3.0
438 stars 63 forks source link

[反面教材] 自造 Handler 的后果 #9

Closed SerinaNya closed 4 years ago

SerinaNya commented 4 years ago

我试图在 GroupMessage 的监听器中加入自己写的命令处理器,可结果并不如人意,Graia 似乎并没有正确地调用 group_message_listener 方法(至少我在 bot.pyprint('OK') 是失败的),请问这是框架的问题还是我的问题呢?如果问题在我,我该如何调整以达到目的?


bot.py

import asyncio

from graia.application.entry import (
    GraiaMiraiApplication, MessageChain,
    Member, Group, GroupMessage,
    Plain, At, Image
)
from graia.broadcast import Broadcast

from botpermissions import Permissions
from botmessages import BotMessage

bot_event_mapping = dict
bot_command = str

loop = asyncio.get_event_loop()
bcc = Broadcast(loop=loop)

class Bot(object):
    app: GraiaMiraiApplication

    member_join_event_mapping: bot_event_mapping = dict()
    group_message_commands_mapping: bot_event_mapping = dict()

    def __init__(self, connect_info: GraiaMiraiApplication) -> None:
        self.app = GraiaMiraiApplication(
            broadcast=bcc,
            connect_info=connect_info
        )

    def run(self) -> None:
        self.app.launch_blocking()

    def _findCmdFunc(self, cmd: bot_command):
        _mapping = self.group_message_commands_mapping
        if cmd in _mapping:
            return _mapping[cmd]
        else:
            return None

    def onCommand(self, cmd: bot_command):
        def deco(f):
            self.group_message_commands_mapping[cmd] = f
            print(self.group_message_commands_mapping)
            return f
        return deco

    @bcc.receiver("GroupMessage")
    async def group_message_listener(self, app: GraiaMiraiApplication, event: GroupMessage, group: Group):
        print('OK')
        bMessage = BotMessage(event)
        if bMessage.msg.cmd and not bMessage.permission.isBlocked:  # 如果有指令(带 $)且没有被封禁
            targetFunc = self._findCmdFunc(bMessage.cmd.cmd)
            print(targetFunc)
            messageList = targetFunc(bMessage=bMessage)
            await app.sendGroupMessage(group, MessageChain.create(messageList))

main.py

from bot import Bot
import settings

from graia.application.entry import (
    Plain, At, Image
)

bot = Bot(settings.connection_session)

@bot.onCommand(cmd='$test')
def test(bMessage) -> list:
    return [Plain('Nya!')]

if __name__ == "__main__":
    bot.run()

botmessage.py

from graia.application.entry import (
    Member, GroupMessage,
    MessageChain, Plain, At, Quote
)

from settings import SpecialQqNums
from botpermissions import Permissions

class BotMessage(object):
    '''MessageChain 解析器'''
    message_chain: MessageChain
    sender: Member

    permission: Permissions

    class msg(object):
        display: str = None
        plain: list = None
        at: list = None

    class quote(object):
        display: str = None

    class cmd(object):
        cmd: str = None
        args: list = None

    def __init__(self, _group_message: GroupMessage):
        '''初始化 BotMessage

        Args:
            _group_message: GroupMessage 对象 '''
        # 初始化
        self.message_chain = _group_message.messageChain
        self.sender = _group_message.sender

        # 设置对象属性
        self._setMsg()
        self._setCmd()
        self._setQuote()

    def _setMsg(self):
        '''设置消息本身'''
        self.msg.plain = self.message_chain.get(Plain)
        self.msg.at = self.message_chain.get(At)
        self.msg.display = self._getPlainsDisplay(self.msg.plain)

        # Constance 优化
        if self.sender.id == SpecialQqNums.constance:
            self.msg.display = self.msg.display.split(':', 1)[1]  # :后的为消息部分

    def _setQuote(self):
        '''设置被回复的原文'''
        _quote: Quote = self.message_chain.get(Quote)[0]  # Quote 只会有一个
        self.quote.display = self._getQuotePlainsDisplay(_quote.origin)

    def _setCmd(self):
        '''设置 cmd 相关'''
        _msg_plain = self.msg.display
        if '$' in _msg_plain:
            _spilted_message = _msg_plain.split(' ', 1)
            self.cmd.cmd = _spilted_message[0]
            self.cmd.args = _spilted_message[1] if len(
                _spilted_message) > 1 else None

    @staticmethod
    def _getPlainsDisplay(l: list) -> str:
        text = ''
        for _i in l:
            i_text = _i.text
            if _i.text.strip() != '':
                text = f'{text} {i_text}'
        return text.strip()

    @classmethod
    def _getQuotePlainsDisplay(cls, l: list):
        i_l = list()
        for _i in l:
            if isinstance(_i, Plain):
                i_l.append(_i)
        return cls._getPlainsDisplay(i_l)
GreyElaina commented 4 years ago

又是自造 handler, 操了.

停止自己自造 handler 这种愚蠢到爆炸的行为, broadcast control 十分强大, 具体的内容我会在今天晚上更新的文档里面说明, 就拿你这个例子

SerinaNya commented 4 years ago

又是自造 handler, 操了.

停止自己自造 handler 这种愚蠢到爆炸的行为, broadcast control 十分强大, 具体的内容我会在今天晚上更新的文档里面说明, 就拿你这个例子

啊,那真是太棒了!i了i了

GreyElaina commented 4 years ago

我知道你这个问题出在哪了, 这个问题我也在使用 atexit.register 时发生了.

你需要把那行愚蠢的 @bcc.receiver 去除, 然后在 __init__ 里面写:

bcc.receiver("GroupMessage")(self.group_message_listener)

问题很jb简单, bcc.receiver 被执行时, 传入的是他妈的一个还没被 classy 的 function, 其中的 self 极有可能导致了 RequirementCrashed 错误.

GreyElaina commented 4 years ago

求求你们别他妈自己造 Handler 了, 我马上出一个文档给你们说一下该如何使用 无头参数装饰器(Headless Decorator)

SerinaNya commented 4 years ago

求求你们别他妈自己造 Handler 了, 我马上出一个文档给你们说一下该如何使用 无头参数装饰器(Headless Decorator)

那您是建议我用 无头参数装饰器?这个更适合是吗?

GreyElaina commented 4 years ago

求求你们别他妈自己造 Handler 了, 我马上出一个文档给你们说一下该如何使用 无头参数装饰器(Headless Decorator)

那您是建议我用 无头参数装饰器?这个更适合是吗?

你这个是纯属瞎几把又乱造了一个 Broadcast.listeners, 并且你这个也只是类似对 Listener 进行过滤, 使他在应有的地方启动, 这与我的设计中的无头参数装饰器是有一定的重合的, 并且如果你使用这个特性, 或许还可以通过后续更新中的, 命名空间(Namespace) 层面的无头参数装饰器更进一步的优化.

GreyElaina commented 4 years ago

对于你后面这个 BotMessage: 赶紧!去死啊啊啊啊啊(╯‵□′)╯︵┻━┻

SerinaNya commented 4 years ago

对于你后面这个 BotMessage: 赶紧!去死啊啊啊啊啊(╯‵□′)╯︵┻━┻

那我 BotMessage 要动吗

GreyElaina commented 4 years ago

我只对你后面的这个自造 listener 集合批判, 两个设计都不够好, 但自造 listener 集合看起来已经成为一个非常广泛的现象了...我希望能改变这个怪现象

SerinaNya commented 4 years ago

我只对你后面的这个自造 listener 集合批判, 两个设计都不够好, 但自造 listener 集合看起来已经成为一个非常广泛的现象了...我希望能改变这个怪现象

看起来还得继续增强文档 :thinking: 没有文档大家咋知道这么用嘛

GreyElaina commented 4 years ago

我只对你后面的这个自造 listener 集合批判, 两个设计都不够好, 但自造 listener 集合看起来已经成为一个非常广泛的现象了...我希望能改变这个怪现象

看起来还得继续增强文档 🤔 没有文档大家咋知道这么用嘛

我也不知道该咋增强文档了, 但我有一点可以明确:

SerinaNya commented 4 years ago

不如把一些愚蠢的操作列一下:

坐等你 commit 文档了((

GreyElaina commented 4 years ago

已经出了一部分, 另外两个我明天写

SerinaNya commented 4 years ago

判断 MessageChain 中是否有 command 可以用无头参数修饰器来解决,那么今天的文档中是否会说明如何监听具体 command 并执行相关业务代码呢?要知道,command 是 bot 的一个重要组成部分,大多数 bot 都有类似的功能,所以,我还是希望能讲一下这种情况应该怎样处理。

SerinaNya commented 4 years ago

我知道你这个问题出在哪了, 这个问题我也在使用 atexit.register 时发生了.

你需要把那行愚蠢的 @bcc.receiver 去除, 然后在 __init__ 里面写:

bcc.receiver("GroupMessage")(self.group_message_listener)

问题很jb简单, bcc.receiver 被执行时, 传入的是他妈的一个还没被 classy 的 function, 其中的 self 极有可能导致了 RequirementCrashed 错误.

当我尝试这个临时的解决方案后,我发现在我发出 $test 后,bot 依然没有回应,控制台也没有任何响应

我上传了部分的源代码,由于 settings.py 含有敏感信息,暂时不便提供

jinzhijie-202008030754.zip

GreyElaina commented 4 years ago

判断 MessageChain 中是否有 command 可以用无头参数修饰器来解决,那么今天的文档中是否会说明如何监听具体 command 并执行相关业务代码呢?要知道,command 是 bot 的一个重要组成部分,大多数 bot 都有类似的功能,所以,我还是希望能讲一下这种情况应该怎样处理。

本来这里的处理是要由模块 graia-ptilopsis 通过构筑标识链解决的, 所以今天的文档中不会包含这个部分(当然, 不排除这个模块突然有想法导致今天内发版的可能)

SerinaNya commented 4 years ago

判断 MessageChain 中是否有 command 可以用无头参数修饰器来解决,那么今天的文档中是否会说明如何监听具体 command 并执行相关业务代码呢?要知道,command 是 bot 的一个重要组成部分,大多数 bot 都有类似的功能,所以,我还是希望能讲一下这种情况应该怎样处理。

本来这里的处理是要由模块 graia-ptilopsis 通过构筑标识链解决的, 所以今天的文档中不会包含这个部分(当然, 不排除这个模块突然有想法导致今天内发版的可能)

哦好的,希望能尽快出来 :)