python - 将装饰器与 Python Slack API 一起使用

标签 python slack-api python-decorators

我尝试创建一个简单的装饰器来与 Slack API 一起使用,以进行进一步的编码,以便以后更容易、更有组织地改进功能(比如说,在将来,我希望包含一个我想要的新函数/命令)机器人要做的事)

但它没有按我的预期工作。让我解释一下,这是我的代码(我省略了大量代码,例如实例化、 token 等,以保持问题简短并说明要点)它包含一个简单的函数,该函数应该在松弛 channel :

class Bot(object):
    def __init__(self): 
        # Bot's user ID in Slack: value is assigned after the bot starts up
        self.starterbot_id = None
        # Declare constants
        self.RTM_READ_DELAY = 1 # 1 second delay between reading from RTM
        self.EXAMPLE_COMMAND = "do"
        self.MENTION_REGEX = "^<@(|[WU].+?)>(.*)"

    def startup(self):
        #Startup the client
        if slack_client.rtm_connect(with_team_state=False):
            print("Slack Bot connected and running!")
            # Read bot's user ID by calling Web API method `auth.test`
            self.starterbot_id = slack_client.api_call("auth.test")["user_id"]
            while True:
                command, channel = self.parse_bot_commands(slack_client.rtm_read())
                if command: 
                    response = self.command(command)
                    self.handle_command(response, channel)
                time.sleep(self.RTM_READ_DELAY)
        else: print("Connection failed. Exception traceback printed above.")

    def handle_command(self, response, channel):
        """
            Executes bot command if the command is known
        """
        # Default response is help text for the user
        default_response = "Not sure what you mean. Try *{}*.".format(self.EXAMPLE_COMMAND)

        # Sends the response back to the channel
        slack_client.api_call("chat.postMessage", channel=channel, text=response or default_response)

    def command(self, func):
        """
            Simple wrapper for bot commands
        """
        def wrapper_command():
            func()
        return wrapper_command

'''USAGE:'''
bot = Bot()
bot.startup()

@bot.command
def greet():
    response = "Hi there I am CodeMonkey"
    return response

我正在寻找的是 greet() 命令来让机器人输出 “嗨,我是 CodeMonkey”

机器人确实做出了回应,但没有打招呼。相反,它返回对象。这是我从 channel 中的机器人得到的响应:

CodeMonkey APP [11:40 PM]
<function Bot.command.<locals>.wrapper_command at 0x1083281e0>

我仍在学习使用装饰器,我觉得它们会让我的 Slack Bot 编码变得更容易、更有组织,但我做错了什么?

最佳答案

嗯,您得到的响应正是人们所期望的:

                response = self.command(command)
                self.handle_command(response, channel)

还有这个:

    def command(self, func):
        """
            Simple wrapper for bot commands
        """
        def wrapper_command():
            func()
        return wrapper_command

您调用self.command(...)并将结果传递给self.handle_command() , self.command()返回一个函数,所以self.handle_command()将此问题设为 response参数。

你还是没有解释你的期望command()用作装饰器时要做的事情,但我假设您想要的是使用装饰函数作为给定“命令”的处理程序(参数,而不是函数 - 提示:对不同的事物使用不同的名称),即 self.command('greet')应该调用greet功能。

您想要的第一件事是使用不同的方法来执行命令并注册它们 - 所以我们将这些方法命名为 execute_commandregister_command - 当我们这样做时,我们会将“handle_command”重命名为“send_response”,因为这就是它真正要做的事情:

def startup(self):
    #Startup the client
    if slack_client.rtm_connect(with_team_state=False):
        print("Slack Bot connected and running!")
        # Read bot's user ID by calling Web API method `auth.test`
        self.starterbot_id = slack_client.api_call("auth.test")["user_id"]
        while True:
            command, channel = self.parse_bot_commands(slack_client.rtm_read())
            if command: 
                response = self.execute_command(command)
                self.send_response(response, channel)
            time.sleep(self.RTM_READ_DELAY)
    else: 
        print("Connection failed. Exception traceback printed above.")


def execute_command(self, command):
    """
        Executes bot command if the command is known
    """
    # mock it for the moment
    return "this is a mockup"  

def send_response(self, response, channel):
    # Default response is help text for the user
    default_response = "Not sure what you mean. Try *{}*.".format(self.EXAMPLE_COMMAND)

    # Sends the response back to the channel
    slack_client.api_call("chat.postMessage", channel=channel, text=response or default_response)

def register_command(self, func):
    """
        TODO
    """

现在是装饰器部分。将函数包装到将在被装饰函数“周围”执行的其他函数中是装饰器的常见用例,但这并不意味着它是强制性的。从技术上讲,“装饰器”只是一个“高阶函数”:一个将函数作为参数并返回函数作为结果的函数。实际上,@decorator语法只是语法糖,所以

@decorate
def func(): 
    pass

只是一种更奇特的写作方式

def func(): 
    pass

func = decorate(func)

一旦你明白了这一点,你也会明白这里绝对没有什么神奇的。

现在回到我们的register_command装饰师。我们想要的是存储参数(一个函数),以便我们可以根据它的名称检索它。这可以通过 dict 和 func.__name__ 轻松完成。 :

 def __init__(self, ...):
     # init code here

     self._commands = {}


def register_command(self, func):
    self._commands[func.__name__] = func
    # and that's all - no wrapper needed here
    return func

现在您可以使用

@bot.register_command:
def greet():
    return "Hello"

然后bot._commands应包含'greet': <function greet @...> .

现在为execute_command方法 - 嗯,它非常简单:查找 self._commands,如果找到某些内容,则调用它并返回响应,否则返回默认响应:

def execute_command(self, command):
    handler = self._commands.get(command)
    if handler:
        return handler()

    # default:
    return "Not sure what you mean. Try *{}*.".format(self.EXAMPLE_COMMAND)

我们现在可以简化send_response()execute_command将负责提供默认响应:

def send_response(self, response, channel):
    # Sends the response back to the channel
    slack_client.api_call("chat.postMessage", channel=channel, text=response)

关于python - 将装饰器与 Python Slack API 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54199411/

相关文章:

python - 如何计算在掷出 1 之前掷出 0 的次数?

python - 如何在 Pycharm 中更改终端提示颜色

python - Slack API 上传片段长度

json - 在 HTTP POST 请求中将 JSON 发送到 Slack

Python 链接装饰器覆盖属性

python - 类装饰器上的 Mypy 注释

python - 以编程方式更改类对象的文档字符串

python - Unitest 用于处理异常的函数

Python Scapy wrpcap - 如何将数据包附加到 pcap 文件?

node.js - API触发消息的头像如何设置?