laravel / slack-notification-channel

Slack Notification Channel for laravel.
https://laravel.com/docs/notifications#slack-notifications
MIT License
864 stars 59 forks source link

[3.0] Support BlockKit / WebAPI-based bot notifications #63

Closed claudiodekker closed 1 year ago

claudiodekker commented 1 year ago

WARNING: THIS PR IS BREAKING.

It introduces a whole new way of interacting with Slack, meaning this is a breaking change. However, it is worth it.

Put simple, this PR "upgrades" the Slack Notification Channel package from simple Webhook-based calls, to WebAPI based calls. This opens up a whole range of new options:

While this WebAPI works based on OAuth tokens, the tokens themselves by default do not have a refresh token, and therefore do not expire either. This essentially makes it technically work the same as a webhook, with the only distinction being that instead of an Unique URL being used, an Authentication Bearer token is used.

Installation

Add this value, and optionally a fallback channel, to your services.php config file:

    'slack' => [
        'notifications' => [
            'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
            'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
        ],
    ],

I did not create a default config/publisher for this, as I don't want to override what the user already has. Not sure what the best approach would be, but (obviously) feel free to adjust how this works if you have a better idea :)

Usage

Write your notification:

<?php

namespace App\Notifications;

use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Slack\BlockKit\Blocks\ActionsBlock;
use Illuminate\Notifications\Slack\BlockKit\Blocks\ContextBlock;
use Illuminate\Notifications\Slack\BlockKit\Composites\ConfirmObject;
use Illuminate\Notifications\Slack\SlackMessage;
use Illuminate\Support\Facades\App;

class BlockKitBasedNotification extends Notification
{
    /**
     * Get the notification channels.
     *
     * @param  mixed  $notifiable
     * @return array|string
     */
    public function via($notifiable)
    {
        return ['slack'];
    }

    /**
     * Get the Notification as a Slack message.
     */
    public function toSlack(object $notifiable): SlackMessage
    {
        return (new SlackMessage())
            ->username('LaraSlackNotifier')
            ->to('#general')
            ->text('This is the fallback notification message')
            ->headerBlock('But first, this message from our sponsor')
            ->actionsBlock(function (ActionsBlock $block) {
                $block->button('Click me!')
                    ->url('https://laravel.com')
                    ->danger()
                    ->confirm('This action can only be undone by gnomes.', function (ConfirmObject $dialog) {
                        $dialog->confirm('You gnome it!');
                        $dialog->deny('Oh no, gnomes!');
                    });
            })
            ->contextBlock(function (ContextBlock $block) {
                $block->image('https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Laravel.svg/1280px-Laravel.svg.png')->alt('The Laravel Logo');
                $block->text('Laravel v' . App::version());
            })
            ->dividerBlock()
            ->imageBlock('https://laravel.com/img/og-image.jpg', 'Laravel Homepage Image');
    }
}

Here's what this Notification will generate:

CleanShot 2023-03-30 at 20 21 24

Then, there's a couple of ways to route the notification when using direct routes:

In the SlackRoute object example, either value is optional/nullable, but this effectively allows you to customize both the OAuth Token, as well as the channel.

Extras

If you quickly want to see what blocks you've been building, you can chain the dd() call on the end of the SlackNotification. This will generate and dump an URL to the BlockKitBuilder, which then previews the payload + the notification itself in your browser. For example, here's the URL to the notification above

If you instead just want the original dd() method and dump the whole payload, you can pass true to the dd() call.

Notes

driesvints commented 1 year ago

@claudiodekker I merged 2.0 into master so it's up to date. Can you resent this PR with a clean commit history? Thanks!