slack-machine icon indicating copy to clipboard operation
slack-machine copied to clipboard

Add support for interactive buttons [block_actions]

Open pawelros opened this issue 1 year ago • 0 comments

Hey! I badly needed support for interactive buttons. So I implemented an early but working version.

What's new:

  • update existing message
  • delete existing message
  • handle block_actions payloads

Example usage:

from machine.plugins.base import MachineBasePlugin
from machine.plugins.decorators import listen_to, process
from slack_sdk.models.blocks.blocks import SectionBlock, ActionsBlock
from slack_sdk.models.blocks.block_elements import ButtonElement


class PasswordReset(MachineBasePlugin):
    """Detects Password reset requests"""

    @listen_to(regex=r"(Password.*reset)|(reset.*Password)")
    async def question(self, msg):
        fields = SectionBlock(
            text=f"Hey <@{msg.sender.id}>, it looks like you might want to reset your password. Do you want me to do it?"
        )

        approve_button = ButtonElement(
            text="Yes, please.",
            action_id="password_reset_yes_for_user",
            value=f"{msg.sender.id}",
            style="primary",
        )
        deny_button = ButtonElement(
            text="No, thank you.",
            action_id="password_reset_no_for_user",
            value=f"{msg.sender.id}",
            style="danger",
        )

        buttons = [approve_button, deny_button]

        actions = ActionsBlock(
            block_id="password_reset_user_confirmation_block", elements=buttons
        )

        await msg.reply(
            # providing text is strongly advised for i.e. mobile notifications
            text=f"Hey <@{msg.sender.id}>, it looks like you might want to reset your password. Do you want me to do it?",
            in_thread=True,
            blocks=[fields, actions],
        )

    @process("block_actions")
    async def update_block(self, event):
        if (
            event.get("actions")
            and event["actions"][0]["block_id"]
            == "password_reset_user_confirmation_block"
        ):
            channel = event["channel"]

           # do more validation, i.e. if the person who clicked the button is allowed to execute the action

            await self.update(
                blocks=[], # reset all blocks to display text
                channel=channel["id"],
                ts=event["message"]["ts"],
                text='Got it',
            )
            # or delete initial message and i.e. post a new one 
            # await self.delete(channel=channel["id"], ts=event["message"]["ts"])

PS. I really tried to delete or update initial message according to https://api.slack.com/interactivity/handling#payloads but I did not manage to do it with socket mode. I am not sure if it is even possible, so I decided to introduce update and delete methods.

Cheers!

pawelros avatar Mar 01 '23 13:03 pawelros