How can I use Apprise to send Adaptive Cards through Teams Workflows?
miguelbalmeida opened this issue · 3 comments
I'm struggling to follow Apprise and Microsoft Teams documentation to do something simple: send a Markdown table as a message to a Teams channel from a Python script.
I've managed to do this before using the Incoming Webhook app in Teams plus Apprise's MS Teams functionality. Sadly, that way of doing things is now deprecated (see the top of the Microsoft link) and new webhooks do not work. Therefore, I am forced to use the new Workflows functionality within Teams.
I have managed to successfully send a message to Teams from within Python using Apprise. However, Markdown table syntax is no longer supported because Microsoft now uses Adaptive Cards. That led me to the new Apprise connector for Microsoft Workflows and Microsoft's documentation on including tables on Adaptive Cards.
What I cannot understand is how I send an Adaptive Card through Apprise from Python. My previous code using the (now deprecated) Incoming Webhook worked like this:
import apprise
teams_webhook = 'https://myorg.webhook.office.com/...'
apprise_object = apprise.Apprise()
apprise_object.add(teams_webhook)
notification_body = """| Heading 1 | Heading 2 | Heading 3 |
|-----------|-----------|-----------|
| Cell A1 | Cell A2 | Cell A3 |
| Cell B1 | Cell B2 | Cell B3 |"""
apprise_object.notify(
title='Flow Test Message',
body=notification_body,
notify_type=apprise.NotifyType.WARNING,
body_format=apprise.NotifyFormat.MARKDOWN,
)
I know that I need to define a JSON object that describes the Adaptive Card I want to send, but the Apprise docs only explain how to send it via CLI, not from Python, and explain it through using templating, which I don't need. I just need to be able to create the JSON object from within Python and send that through Apprise.
What should I pass to the apprise_object.notify()
function to send the Adaptive Card properly?
The templating aspect still works, but you just need to host your template that you want to use (over-riding the default.
import apprise
# point to your template file that
teams_webhook = 'https://myorg.webhook.office.com/...?template=/path/to/template.json'
## You're /path/to/template.json may look like:
# {
# "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
# "type": "AdaptiveCard",
# "version": "1.5",
# "body": [
# {
# "type": "TextBlock",
# "text": "{{app_title}}",
# "weight": "Bolder",
# "separator": true
# },
# {
# "type": "TextBlock",
# "text": "{{app_body}}",
# "wrap": true
# }
# ]
# }
apprise_object = apprise.Apprise()
apprise_object.add(teams_webhook)
apprise_object.notify(
title='Flow Test Message',
body=notification_body,
notify_type=apprise.NotifyType.WARNING,
body_format=apprise.NotifyFormat.MARKDOWN,
)
Or leverage the new format and card you found to build your own template using the structure Microsoft imposes. Optionally use the {{app_body}}
or {{app_title}}
to dynamically populate the fields you're interested in....
For example, maybe your JSON file will look like this (slighly modified from example on Microsoft site you shared):
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "Table",
"gridStyle": "accent",
"firstRowAsHeaders": true,
"columns": [
{
"width": 1
},
{
"width": 1
},
{
"width": 3
}
],
"rows": [
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "Name",
"wrap": true,
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "Type",
"wrap": true,
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "Description",
"wrap": true,
"weight": "Bolder"
}
]
}
],
"style": "accent"
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"style": "good",
"items": [
{
"type": "TextBlock",
"text": "columns",
"wrap": true
}
]
},
{
"type": "TableCell",
"style": "warning",
"items": [
{
"type": "TextBlock",
"text": "ColumnDefinition[]",
"wrap": true
}
]
},
{
"type": "TableCell",
"style": "accent",
"items": [
{
"type": "TextBlock",
"text": "Defines the table's columns (number of columns, and column sizes).",
"wrap": true
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"style": "good",
"items": [
{
"type": "TextBlock",
"text": "rows",
"wrap": true
}
]
},
{
"type": "TableCell",
"style": "accent",
"items": [
{
"type": "TextBlock",
"text": "TableRow[]",
"wrap": true
}
]
},
{
"type": "TableCell",
"style": "attention",
"items": [
{
"type": "TextBlock",
"text": "Defines the rows of the Table, each being a collection of cells. Rows are not required, which allows empty Tables to be generated via templating without breaking the rendering of the whole card.",
"wrap": true
}
]
}
]
}
]
},
{
"type": "Table",
"gridStyle": "accent",
"firstRowAsHeaders": true,
"showGridLines" : false,
"columns": [
{
"width": 1
},
{
"width": 1
},
{
"width": 3
}
],
"rows": [
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "Name",
"wrap": true,
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "Type",
"wrap": true,
"weight": "Bolder"
}
]
},
{
"type": "TableCell",
"items": [
{
"type": "TextBlock",
"text": "{{app_title}}",
"wrap": true,
"weight": "Bolder"
}
]
}
],
"style": "accent"
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"style": "good",
"items": [
{
"type": "TextBlock",
"text": "columns",
"wrap": true
}
]
},
{
"type": "TableCell",
"style": "warning",
"items": [
{
"type": "TextBlock",
"text": "ColumnDefinition[]",
"wrap": true
}
]
},
{
"type": "TableCell",
"style": "accent",
"items": [
{
"type": "TextBlock",
"text": "Defines the table's columns (number of columns, and column sizes).",
"wrap": true
}
]
}
]
},
{
"type": "TableRow",
"cells": [
{
"type": "TableCell",
"style": "good",
"items": [
{
"type": "TextBlock",
"text": "rows",
"wrap": true
}
]
},
{
"type": "TableCell",
"style": "accent",
"items": [
{
"type": "TextBlock",
"text": "TableRow[]",
"wrap": true
}
]
},
{
"type": "TableCell",
"style": "attention",
"items": [
{
"type": "TextBlock",
"text": "{{app_body}}",
"wrap": true
}
]
}
]
}
]
}
]
}
Can i close our this ticket? I haven't heard back from you in a bit.
Closing having not heard back.