laravel-notification-channels / microsoft-teams

Microsoft Teams Notifications Channel for Laravel
https://laravel-notification-channels.com
MIT License
136 stars 15 forks source link

O365 connectors within Teams will be deprecated #30

Open xolf opened 2 months ago

xolf commented 2 months ago

Hey,

since today Teams appends the following messages automatically to messages send via this package:

Action Required: O365 connectors within Teams will be deprecated and notifications from this service will stop. Learn more about the timing and how the Workflows app provides a more flexible and secure experience. If you want to continue receiving these types of messages, you can use a workflow to post messages from a webhook request. Set up workflow

Are there any guides or tips for an migration?

EdyStauch commented 2 months ago

I tried creating a "workflow" as indicated in the message, and using the URL provided but it doesn't seem to work.

jbraband commented 2 months ago

I tried creating a "workflow" as indicated in the message, and using the URL provided but it doesn't seem to work.

I did the same and am getting a 400 Rad Request response with nothing else as to the cause.

xolf commented 2 months ago

Same here. The integrated Workflow in Teams didn't worked for me. I created a Power Automate Flow with an HTTP Endpoint (requires Premium subscription) and then send the messages via the Microsoft Teams connector to Teams.

One benefit of this solution is, you can also target individual users or group chats, in addition to channels.

OGLECB commented 2 months ago

I have done some research, it looks like Microsoft has changed the format of the payload - So I think some changes will need to be made to the code.

jsandfordhughescoop commented 1 month ago

I have been doing some research on this, the new 'workflow' way is not very well documented. I have managed to get a JSON webhook to create an 'AdaptiveCard'.

The tricky part is getting the workflow setup correctly.

First head into teams and create a new workflow, start a blank workflow. For the trigger, you want to search for webhook and select the Microsoft Teams Webhook.

Next add a step to the workflow, it should look like this so far:

CleanShot 2024-07-11 at 10 07 53@2x

Now search for "Apply to each" and select it as an action. In the "Select an output" box you want to choose the dynamic content from the righthand side, select "Attachments". It should now look like this:

CleanShot 2024-07-11 at 10 09 17@2x

Next add another action and find "Post card in chat or channel" and fill in the details for your required destination. (Make sure you choose the correct "Post as" option as flow bots cannot post into private channels).

You should now have something like this:

CleanShot 2024-07-11 at 10 12 27@2x

Now in the adaptive chat box you need to create an "expression" and in the expression you need to put the following "item().content". It should look like the following:

CleanShot 2024-07-11 at 10 16 58@2x

Hit okay on the expression and it will put it into the adaptive card box, then save the workflow.

Once saved you can click on the webhook trigger at the top and copy the webhook url.

Now send a POST request to the webhook with the JSON below

{ "attachments": [ { "contentType": "application/vnd.microsoft.card.adaptive", "contentUrl": null, "type": "AdaptiveCard", "content": { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.2", "body": [ { "type": "TextBlock", "wrap": true, "text": "This is the text of a card" } ], "actions": [ { "type": "Action.OpenUrl", "title": "Github URL", "url": "https://github.com" } ] } } ] }

If you format the JSON you will see that there is an attachments array and the attachment has a content object. This content object is a valid schema for AdaptiveCards.io. You can view the schema here: AdaptiveCards.io.

I know this does not currently fix the issue with the package however it gives a good head start to someone wanting to create a PR. If I can get around to it, I will work on the PR myself.

If anyone needs anymore info, please let me know.

jbraband commented 1 month ago

@jsandfordhughescoop awesome tutorial. I was able to build the workflow as you describe and get your test POST request to show the card in the Teams channel.

I could not get the older MessageCard (that is built in the class \NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage) to work through this.

Has anyone used this repo before? https://github.com/sebbmeyer/php-microsoft-teams-connector

It has a class that builds the AdaptiveCard schema and also a way to send it to a Teams webhook endpoint. I like using the Laravel Notification system for things like this, but what would be the chances of a PR using sebbmeyer/php-microsoft-teams-connector to be merged into this repo?

jbraband commented 1 month ago

Looks like maybe @Tob0t is the main maintainer of this repo. @Tob0t do you have an opinion on expanding this package to handle the AdaptiveCard schema?

jbraband commented 1 month ago

Just a note that Message Cards are marked as Legacy in the Microsoft documentation

https://learn.microsoft.com/en-us/outlook/actionable-messages/message-card-reference

jglagrimas commented 1 month ago

So any idea if the workflow is disabled on our organization? any alternatives?

jsandfordhughescoop commented 1 month ago

So any idea if the workflow is disabled on our organization? any alternatives?

A quick google and it does not seem disabling workflows is an option in Azure / teams admin

jglagrimas commented 1 month ago

image @jsandfordhughescoop

jsandfordhughescoop commented 1 month ago

Thats interesting! Sorry nothing came up in a google search. As far as I am aware, teams are EOL all "connectors" so the only way going forward are workflows.

Maybe chat to your admins and see what they can do?

jglagrimas commented 1 month ago

Thats interesting! Sorry nothing came up in a google search. As far as I am aware, teams are EOL all "connectors" so the only way going forward are workflows.

Maybe chat to your admins and see what they can do?

thanks @jsandfordhughescoop

i will reach to them.

OGLECB commented 1 month ago

Thats interesting! Sorry nothing came up in a google search. As far as I am aware, teams are EOL all "connectors" so the only way going forward are workflows. Maybe chat to your admins and see what they can do?

thanks @jsandfordhughescoop

i will reach to them.

@jglagrimas Your organisation looks like they have set up conditional access to the Power Automate Workflows - hence why you are seeing it as disabled by the orgainisation.

image

Issa-projects commented 1 month ago

I have a current working workarround. Create a new class to extend MicrosoftTeamsMessage:


<?php

namespace App\Services\MicrosoftTeams;

use NotificationChannels\MicrosoftTeams\MicrosoftTeamsMessage;

class TeamsNotification extends MicrosoftTeamsMessage
{
    protected $payload = [];
    protected string $type = "Good";

    /** @var string webhook url of recipient. */
    protected $webhookUrl = null;

    public function __construct()
    {
        parent::__construct();
        $this->payload = [
            "\$schema" => "https://adaptivecards.io/schemas/adaptive-card.json",
            "type" => "AdaptiveCard",
            "version" => "1.0",
        ];

    }

    public static function create(string $content = ''): self
    {
        return new self();
    }

    public function title(string $title, array $params = []): self
    {
        $this->payload["body"][] = [
            "type" => "TextBlock",
            "wrap" => true,
            "style" => "heading",
            "text" => $title,
            "weight" => "bolder",
            "size" => "large",
        ];

        return $this;
    }

    public function type(string $type): self
    {
        $types = ["info" => "Good", "error" => "Attention", "warning" => "Warning", "success" => "Good"];
        $this->type = $types[$type];

        return $this;
    }

    /**
     * @throws \Exception
     */
    public function to(?string $webhookUrl): self
    {
        if (!$webhookUrl) {
            throw new \Exception("Webhook url is required. Tried to send a teams notification without a webhook url.");
        }
        $this->webhookUrl = $webhookUrl;

        return $this;
    }

    public function content(string $content, array $params = []): self
    {
        // add the content as a new text block
        $this->payload["body"][] = [
            "type" => "TextBlock",
            "wrap" => true,
            "text" => $content,
            "size" => "medium",
            "separator" => true,

        ];
        return $this;
    }

    public function button(string $text, string $url = '', array $params = []): self
    {
        $this->payload["actions"][] = [
            "type" => "Action.OpenUrl",
            "title" => $text,
            "url" => $url,
            "style" => "positive",
        ];

        return $this;
    }

    public function addStartGroupToSection($sectionId = 'standard_section'): self
    {

        return $this;
    }

    public function fact(string $name, string $value, $sectionId = 'standard_section'): self
    {
        $factset = collect($this->payload['body'])->filter(function ($item) {
            return $item['type'] === "FactSet";
        });
        if($factset->isEmpty()) {
            $factset = [
                "type" => "FactSet",
                "facts" => [],
            ];
        }
        $factset['facts'][] = [
            "title" => $name,
            "value" => $value,
        ];

        $this->payload['body'][] = $factset;
        return $this;
    }
    public function getWebhookUrl(): string
    {
        info("Sending to teams wb: $this->webhookUrl");
        return $this->webhookUrl;
    }
    public function toArray(): array
    {
        // check and get the first text block and set the color to the type
         if($this->payload["body"][0] && $this->payload["body"][0]["type"] === "TextBlock") {
             $this->payload["body"][0]["color"] = $this->type;
         }

        return $this->payload;
    }
}

And now, instead of:

return MicrosoftTeamsMessage::create()
    ->to(config('services.microsoft_teams.sales_url'))
    ->type('success')
    ->title('Subscription Created')
    ->content('Yey, you got a **new subscription**. Maybe you want to contact him if he needs any support?')
    ->button('Check User', 'https://foo.bar/users/123');

Do this:

return TeamsNotification::create()
    ->to(config('services.microsoft_teams.sales_url'))
    ->type('success')
    ->title('Subscription Created')
    ->content('Yey, you got a **new subscription**. Maybe you want to contact him if he needs any support?')
    ->button('Check User', 'https://foo.bar/users/123');

For creating a new workflow in teams:

Go to teams -> click on the three dots on the left side and select workflows> image

image

In the "Adaptive Card" field, make sure to choose expression and use triggerBody() as value.

peterthomson commented 2 weeks ago

Ok, so it seems that we've all been granted a reprieve by Microsoft from August 2024 until December 2025 (although we'll have to make a URL change in December 2024 to opt-in). But long-term, it certainly sounds like the Webhook to Workflow pathway is going to be the only option for sending notifications to Teams. And that will in turn probably require the Adaptive Card format. More info here: https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/

In terms of a way forwards for the Notification Channel driver approach (this package) we can take inspiration from a couple of packages that have leaned into the Workflow pattern (and Adaptive Cards): https://github.com/osa-eg/laravel-teams-notification https://github.com/sebbmeyer/php-microsoft-teams-connector/

Tob0t commented 1 week ago

Hi, sorry for the late response 🙈 Since I am no longer actively using teams, I could not find (free)time to migrate to prepare an alternative method. I have no strong opinion towards which way to go, but I am open to suggestions (or even better, a PR ;)) This package is open source, so feel free to create a PR - I am willing to review and merge it if it is a satisfying solution.