-
-
Save chusiang/895f6406fbf9285c58ad0a3ace13d025 to your computer and use it in GitHub Desktop.
#!/bin/bash | |
# ============================================================================= | |
# Author: Chu-Siang Lai / chusiang (at) drx.tw | |
# Filename: teams-chat-post-for-workflows.sh | |
# Modified: 2024-07-22 11:44 (UTC+08:00) | |
# Description: Post a message to Microsoft Teams via "Post to a chat when a webhook request is received" workflows. | |
# Reference: | |
# | |
# - https://gist.github.com/chusiang/895f6406fbf9285c58ad0a3ace13d025 | |
# - https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/ | |
# - https://adaptivecards.io/explorer/ | |
# - https://adaptivecards.io/designer/ | |
# | |
# ============================================================================= | |
# Help. | |
if [[ "$1" == "-h" || "$1" == "--help" ]]; then | |
echo 'Usage: teams-chat-post.sh "<webhook_url>" "<title>" "<color>" "<message>"' | |
exit 0 | |
fi | |
# Webhook or Token. | |
WEBHOOK_URL=$1 | |
if [[ "${WEBHOOK_URL}" == "" ]]; then | |
echo "No webhook_url specified." | |
exit 1 | |
fi | |
shift | |
# Title . | |
TITLE=$1 | |
if [[ "${TITLE}" == "" ]]; then | |
echo "No title specified." | |
exit 1 | |
fi | |
shift | |
# Color. | |
COLOR=$1 | |
if [[ "${COLOR}" == "" ]]; then | |
echo "No color specified." | |
exit 1 | |
fi | |
shift | |
# Text. | |
TEXT=$* | |
if [[ "${TEXT}" == "" ]]; then | |
echo "No text specified." | |
exit 1 | |
fi | |
# Escape char: `'`, `"`, `\` . | |
MESSAGE=$(echo ${TEXT} | sed "s/'/\'/g" | sed 's/"/\"/g; s/\\/\\\\/g') | |
# Adaptive Cards of TextBlock - https://adaptivecards.io/explorer/TextBlock.html | |
JSON="{ | |
\"type\": \"message\", | |
\"attachments\": [ | |
{ | |
\"contentType\": \"application/vnd.microsoft.card.adaptive\", | |
\"contentUrl\": null, | |
\"content\": { | |
\"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\", | |
\"type\": \"AdaptiveCard\", | |
\"version\": \"1.2\", | |
\"body\": [ | |
{ | |
\"type\": \"TextBlock\", | |
\"text\": \"${TITLE}\", | |
\"color\": \"${COLOR}\", | |
\"weight\": \"bolder\", | |
\"size\": \"large\", | |
\"wrap\": true | |
}, | |
{ | |
\"type\": \"TextBlock\", | |
\"text\": \"${MESSAGE}\", | |
\"color\": \"${COLOR}\", | |
\"size\": \"small\", | |
\"wrap\": true | |
} | |
] | |
} | |
} | |
] | |
}" | |
# Post to Microsoft Teams via curl. | |
curl \ | |
--header "Content-Type: application/json" \ | |
--request POST \ | |
--data "${JSON}" \ | |
"${WEBHOOK_URL}" |
#!/bin/bash | |
# ============================================================================= | |
# Author: Chu-Siang Lai / chusiang (at) drx.tw | |
# Filename: teams-chat-post.sh | |
# Modified: 2024-07-12 18:49 (UTC+08:00) | |
# Description: Post a message to Microsoft Teams via connectors, not support | |
# Power Automate workflows. | |
# Reference: | |
# | |
# - https://gist.github.com/chusiang/895f6406fbf9285c58ad0a3ace13d025 | |
# - https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/ | |
# - Fixed for workflows edition: https://gist.github.com/chusiang/895f6406fbf9285c58ad0a3ace13d025?permalink_comment_id=5119162#gistcomment-5119162 | |
# | |
# ============================================================================= | |
# Help. | |
if [[ "$1" == "-h" || "$1" == "--help" ]]; then | |
echo 'Usage: teams-chat-post.sh "<webhook_url>" "<title>" "<color>" "<message>"' | |
exit 0 | |
fi | |
# Webhook or Token. | |
WEBHOOK_URL=$1 | |
if [[ "${WEBHOOK_URL}" == "" ]]; then | |
echo "No webhook_url specified." | |
exit 1 | |
fi | |
shift | |
# Title . | |
TITLE=$1 | |
if [[ "${TITLE}" == "" ]]; then | |
echo "No title specified." | |
exit 1 | |
fi | |
shift | |
# Color. | |
COLOR=$1 | |
if [[ "${COLOR}" == "" ]]; then | |
echo "No status specified." | |
exit 1 | |
fi | |
shift | |
# Text. | |
TEXT=$* | |
if [[ "${TEXT}" == "" ]]; then | |
echo "No text specified." | |
exit 1 | |
fi | |
# Convert formating. | |
MESSAGE=$(echo ${TEXT} | sed 's/"/\"/g' | sed "s/'/\'/g") | |
JSON="{ | |
\"title\": \"${TITLE}\", | |
\"themeColor\": \"${COLOR}\", | |
\"text\": \"${MESSAGE}\" | |
}" | |
# Post to Microsoft Teams. | |
curl -H "Content-Type: application/json" -d "${JSON}" "${WEBHOOK_URL}" | |
Thanks @ronnyheymans The wrap: true will help me for now. I will keep looking myself for options to enlarge the Adaptive card
If you create a new flow with workflows and change the json structure of the script it works again
create flow with following steps :
- Open Workflows in teams
- +new flow
- choose notifications at the left
- select "Post to a channel when a webhook request is received"
- at the set up your flow part choose the team and channel where you want to post
- create the flow
change ths JSON part in the script to the following : JSON="{"type":"message","attachments":[{"contentType":"application/vnd.microsoft.card.adaptive","contentUrl":null,"content":{"$schema":"http://adaptivecards.io/schemas/adaptive-card.json","type":"AdaptiveCard","version":"1.2","body":[{"type": "TextBlock","text": "${TITLE}"},{"type": "TextBlock","text": "${MESSAGE}"}]}}]}"
More info on the structure of an adaptive card : https://learn.microsoft.com/en-us/adaptive-cards/authoring-cards/getting-started More info on webhook json body structure : https://learn.microsoft.com/en-us/connectors/teams/?tabs=text1#example Adaptive Card designer : https://adaptivecards.io/designer/
Yes, it works.
What about "themeColor": "${COLOR}"? I tried adding this parameter, however, nothing happened.
@whanklee You can add \"color\": \"<options>\"
are:
Default
Dark
Light
Accent
Good
Warning
Attention
@q2uantum Thank you for your share, I can send message to Chat via Workflows now, but I have no permission to send to Channel, so I can't verify it now.
I have put the script of teams-chat-post-for-workflows.sh .
[ [email protected] ~]
$ ./teams-chat-post-for-workflows.sh "https://prod-XXX.weXXXX.logic.azure.com:443/workflows/1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXf/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=BXXXXXXXXXF-fXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4" "Workflows" "Default" "Send from XXXXX-ubuntu."
@chusiang You have a typo in the script :
{ "type": "TextBlock", "text": "${MESSAGE}", "warp": true } should be
{ "type": "TextBlock", "text": "${MESSAGE}", "wrap": true }
@ronnyheymans Thank you for feedback, I have fixed it.
I have leaved a comment to share this new script on Retirement of Office 365 connectors within Microsoft Teams | Microsoft Developer Blogs post, maybe it can help someone.
@chusiang thanks for your post, any idea how to get rid of the footnote message "... used a Workflow template to send this card. Get template" and to change the Workflow logo ?
Good news, I can send notification with teams-chat-post-for-workflows.sh to channel via workflows !
I guess, it's working now, because I am the owner role in this Teams.
Thanks chusiang , is it possible remove the user name from message ?
Hi @wengzy , I think you need the title like "chusiang via Workflows", I can not see some options for it now.
I'm also successfully using your script to post, but my backend monitoring tool has checks against Windows drive pathing. When those checks pass their data to ${TITLE} or ${MESSAGE} it contains a \ in the drive path. This is breaking the JSON with a "Bad JSON escape sequence: \ . Path 'attachments[0].content.body[1].text" error. I've tried a couple of things to correct this issue, but haven't been able to get around it. Any thoughts?
Hi @lp-maximus,
I think we need add Escape Character for \
on L60 in teams-chat-post-for-workflows.sh.
I have also escape the \
char, maybe it can help you.
- MESSAGE=$(echo ${TEXT} | sed 's/"/\"/g' | sed "s/'/\'/g")
+ MESSAGE=$(echo ${TEXT} | sed "s/'/\'/g" | sed 's/"/\"/g; s/\\/\\\\/g')
Good luck.
Reference:
Chusiang,
Thanks for sharing your script. I was able to look at the JSON you used in your scripts and back-track it to a Microsoft Article that had the working sample JSON for incoming webhooks and most of the requirements for making a successful Adaptive Card post in Teams. There are a lot of samples for AdaptiveCard from Microsoft articles that do not work as-is in Teams. They must have the required bits from the article The JSON in the incoming webhook section below is the only JSON formatting that's been successful so far for me.
In combination with using the "body" sections of the samples from AdaptiveCards.io, I've made a lot of progress in posting various types of messages with different formatting. The examples that used data binding don't seem to be supported in Teams. The article above also calls out the elements in the schema that are and are not supported.
Additionally, for those using GitLab the webhooks work from there if the workflow accepts POSTS from "anyone"
@chusiang,
Your code changes worked great for me, much appreciated.
@chusiang thanks for your post, any idea how to get rid of the footnote message "... used a Workflow template to send this card. Get template" and to change the Workflow logo ?
You can remove the message by copying the Workflows to a new one. A new webhook url will be created. I have the original one turned off.
Additionally, if you create the workflow without using a template, the footer advertisement for templates won’t be present. I think it’s there to promote the use of workflow templates to save time. You can assemble the same components that are in the template and not have to worry about the footer advertisement.
Thanks for feedback from @cowbe0x004 and @ehouston3 ,
I have tried to remove the footer, but something is working and something not.
If we want to stop show (or remove) the footer:
- Manual create the workflows, not from template. #gistcomment-5131921
- Clone the working one from template, and use the new one. #gistcomment-5131697
I think the answer is clean now.
CC @zeyt27
This is beautiful. Thank you!
How to send a message on Microsoft teams with new line character? With \n not working. What should I use?
Hi @whanklee , please try \n\n
, it's working on https://adaptivecards.io/designer .
{
"type": "TextBlock",
"text": "It doesn't wrap by\n\n default",
"weight": "bolder"
},
Hi @chusiang,
\r\n\r\n works well.
Hi @lp-maximus,
I think we need add Escape Character for
\
on L60 in teams-chat-post-for-workflows.sh.I have also escape the
\
char, maybe it can help you.- MESSAGE=$(echo ${TEXT} | sed 's/"/\"/g' | sed "s/'/\'/g") + MESSAGE=$(echo ${TEXT} | sed "s/'/\'/g" | sed 's/"/\"/g; s/\\/\\\\/g')Good luck.
Reference:
Hi,
In my case I'm passing to the script an output of another script, which takes sql query result and concats with another line having an escaped newline:
query-db-and-pass-to-teams-script.sh
MESSAGE="$SQL_RESULT" # Which is a 5 line string, 1st 4 ending with literal newlines.
MESSAGE+="\nAnother line here"
${TEAMS_SCRIPT} "$WEBHOOK_URL" "$TITLE" "$COLOR" "$MESSAGE"
So when this $MESSAGE is passed to your script, several issues occur at this line:
MESSAGE=$(echo ${TEXT} | sed "s/'/\'/g" | sed 's/"/\"/g; s/\\/\\\\/g')
- If we use echo ${TEXT} - it will eat all literal newlines. Had to use echo "${TEXT}" to avoid that.
- s/\/\\/g turns my concated escaped newline in "\nAnother line here" into
"\\\\nAnother line here"
P.S.
echo "No status specified."
Was it meant "No color specified."?
Hi @alaxelmck,
I have update to No color specified.
, it's typo with I copy from my old script like slack-chat-post.sh
.
About support output of another script part, I have no idea now.
I have done a rewrite of the script:
- Use
jq
for managing JSON - Use
getopts
- Added a bunch of optional parameters
#!/bin/bash
# =============================================================================
# Original Author: Chu-Siang Lai / chusiang (at) drx.tw
# Modifier: @emibcn https://github.com/emibcn
# Filename: teams-chat-post-for-workflows.sh
# Description: Post a message to Microsoft Teams via "Post to a chat when a webhook request is received" workflows.
# Reference:
#
# - https://gist.github.com/chusiang/895f6406fbf9285c58ad0a3ace13d025
# - https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/
# - https://adaptivecards.io/explorer/
# - https://adaptivecards.io/designer/
#
# =============================================================================
set -Eeuo pipefail
usage() {
cat <<EOF
Usage:
teams-chat-post.sh -w "<webhook_url>" -t "<title>" -x "<message>"
Optional parameters:
-h Get usage help (this text)
-c "<Color>" (default: "default")
-u "<Button URL>" If not used, no button is shown
-b "<Button Text>" (default: "URL")
-i "<IMAGE_URL>" If not used, no image is shown
-p "<Expandable title>" Used for the expand button (Default: "View more")
-e "<Expandable text>" If not used, no expandable text is shown
-l "<Expandable logo>" Used as logo for the "View more" button
EOF
exit ${1:-0}
}
# Get options into variables
while getopts :hw:c:t:x:u:b:i:p:e:l: OPTION
do
case ${OPTION} in
w) WEBHOOK_URL="${OPTARG}" ;;
t) TITLE="${OPTARG}" ;;
x) TEXT="${OPTARG}" ;;
c) COLOR="${OPTARG}" ;;
u) BUTTON_URL="${OPTARG}" ;;
b) BUTTON_TEXT="${OPTARG}" ;;
i) IMAGE_URL="${OPTARG}" ;;
p) EXPANDABLE_TITLE="${OPTARG}" ;;
e) EXPANDABLE_TEXT="${OPTARG}" ;;
l) EXPANDABLE_LOGO="${OPTARG}" ;;
h)
usage
;;
:)
echo "${0}: ERROR: Must supply an argument to -${OPTARG}" >&2
usage 2
;;
?)
echo "${0}: ERROR: Invalid option: -${OPTARG}" >&2
usage 3
;;
esac
done
#
# Verify variables
#
# Webhook or Token
if [ "${WEBHOOK_URL:-}" == "" ]
then
echo "No webhook_url specified."
echo "Visit this URL to see how to obtain one:"
echo "https://support.microsoft.com/en-us/office/creating-a-workflow-from-a-chat-in-teams-e3b51c4f-49de-40aa-a6e7-bcff96b99edc"
usage 1
fi
# Color
# https://adaptivecards.io/explorer/TextBlock.html
ALLOWED_COLORS=(
default
dark
light
accent
good
warning
attention
)
COLOR="${COLOR:-default}"
if [ "${COLOR:-}" == "" ]
then
echo "No color specified."
usage 1
fi
if ! printf '%s\0' "${ALLOWED_COLORS[@]}" | grep -qwz "${COLOR}"
then
echo "The specified color is not in the allowed list:"
echo "$(IFS="," ; echo "${ALLOWED_COLORS[*]}")"
usage 1
fi
# Title
if [ "${TITLE:-}" == "" ]
then
echo "No title specified."
usage 1
fi
# Text
if [ "${TEXT:-}" == "" ]
then
echo "No text specified."
usage 1
fi
# Generate JSON using variables
# Adaptive Cards of TextBlock - https://adaptivecards.io/explorer/TextBlock.html
JSON="$(
jq \
--null-input \
--arg COLOR "${COLOR}" \
--arg TITLE "${TITLE}" \
--arg TEXT "${TEXT}" \
'{
type: "message",
attachments: [
{
contentType: "application/vnd.microsoft.card.adaptive",
contentUrl: null,
content: {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
type: "AdaptiveCard",
version: "1.2",
actions: [],
body: [
{
type: "TextBlock",
text: $TITLE,
color: $COLOR,
weight: "bolder",
size: "large",
wrap: true
},
{
type: "TextBlock",
text: $TEXT,
color: $COLOR,
size: "small",
wrap: true
}
]
}
}
]
}'
)"
if [ "${BUTTON_URL:-}" != "" ]
then
JSON="$(
echo "${JSON}" \
| jq \
--arg URL "${BUTTON_URL}" \
--arg TEXT "${BUTTON_TEXT:-URL}" \
'.attachments[0].content.actions += [
{
type: "Action.OpenUrl",
title: $TEXT,
url: $URL
}
]
'
)"
fi
if [ "${EXPANDABLE_TEXT:-}" != "" ]
then
JSON="$(
echo "${JSON}" \
| jq \
--arg TITLE "${EXPANDABLE_TITLE:-View more}" \
--arg TEXT "${EXPANDABLE_TEXT}" \
--arg URL "${EXPANDABLE_LOGO:-}" \
'.attachments[0].content.actions += [
{
"type": "Action.ShowCard",
"title": $TITLE,
iconUrl: $URL,
"card": {
"type": "AdaptiveCard",
"body": [
{
type: "TextBlock",
text: $TEXT,
size: "small",
wrap: true
}
]
}
}
]
'
)"
fi
if [ "${IMAGE_URL:-}" != "" ]
then
JSON="$(
echo "${JSON}" \
| jq \
--arg IMAGE "${IMAGE_URL}" \
'.attachments[0].content.body = [
{
type: "ColumnSet",
columns: [
{
type: "Column",
items: .attachments[0].content.body
},
{
type: "Column",
width: "auto",
verticalContentAlignment: "center",
items: [
{
type: "Image",
url: $IMAGE
}
]
}
]
}
]
'
)"
fi
# Post to Microsoft Teams using curl
curl \
-v \
--header "Content-Type: application/json" \
--request POST \
--data "${JSON}" \
"${WEBHOOK_URL}"
I have done a rewrite of the script:
- Use
jq
for managing JSON- Use
getopts
- Added a bunch of optional parameters
#!/bin/bash # ============================================================================= # Original Author: Chu-Siang Lai / chusiang (at) drx.tw # Modifier: @emibcn https://github.com/emibcn # Filename: teams-chat-post-for-workflows.sh # Description: Post a message to Microsoft Teams via "Post to a chat when a webhook request is received" workflows. # Reference: # # - https://gist.github.com/chusiang/895f6406fbf9285c58ad0a3ace13d025 # - https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/ # - https://adaptivecards.io/explorer/ # - https://adaptivecards.io/designer/ # # ============================================================================= set -Eeuo pipefail usage() { cat <<EOF Usage: teams-chat-post.sh -w "<webhook_url>" -t "<title>" -x "<message>" Optional parameters: -h Get usage help (this text) -c "<Color>" (default: "default") -u "<Button URL>" If not used, no button is shown -b "<Button Text>" (default: "URL") -i "<IMAGE_URL>" If not used, no image is shown -p "<Expandable title>" Used for the expand button (Default: "View more") -e "<Expandable text>" If not used, no expandable text is shown -l "<Expandable logo>" Used as logoo for the "View more" button EOF exit ${1:-0} } # Get options into variables while getopts :hw:c:t:x:u:b:i:p:e:l: OPTION do case ${OPTION} in w) WEBHOOK_URL="${OPTARG}" ;; t) TITLE="${OPTARG}" ;; x) TEXT="${OPTARG}" ;; c) COLOR="${OPTARG}" ;; u) BUTTON_URL="${OPTARG}" ;; b) BUTTON_TEXT="${OPTARG}" ;; i) IMAGE_URL="${OPTARG}" ;; p) EXPANDABLE_TITLE="${OPTARG}" ;; e) EXPANDABLE_TEXT="${OPTARG}" ;; l) EXPANDABLE_LOGO="${OPTARG}" ;; h) usage ;; :) echo "${0}: ERROR: Must supply an argument to -${OPTARG}" >&2 usage 2 ;; ?) echo "${0}: ERROR: Invalid option: -${OPTARG}" >&2 usage 3 ;; esac done # # Verify variables # # Webhook or Token if [ "${WEBHOOK_URL:-}" == "" ] then echo "No webhook_url specified." echo "Visit this URL to see how to obtain one:" echo "https://support.microsoft.com/en-us/office/creating-a-workflow-from-a-chat-in-teams-e3b51c4f-49de-40aa-a6e7-bcff96b99edc" usage 1 fi # Color # https://adaptivecards.io/explorer/TextBlock.html ALLOWED_COLORS=( default dark light accent good warning attention ) COLOR="${COLOR:-default}" if [ "${COLOR:-}" == "" ] then echo "No color specified." usage 1 fi if ! printf '%s\0' "${ALLOWED_COLORS[@]}" | grep -qwz "${COLOR}" then echo "The specified color is not in the allowed list:" echo "$(IFS="," ; echo "${ALLOWED_COLORS[*]}")" usage 1 fi # Title if [ "${TITLE:-}" == "" ] then echo "No title specified." usage 1 fi # Text if [ "${TEXT:-}" == "" ] then echo "No text specified." usage 1 fi # Generate JSON using variables # Adaptive Cards of TextBlock - https://adaptivecards.io/explorer/TextBlock.html JSON="$( jq \ --null-input \ --arg COLOR "${COLOR}" \ --arg TITLE "${TITLE}" \ --arg TEXT "${TEXT}" \ '{ type: "message", attachments: [ { contentType: "application/vnd.microsoft.card.adaptive", contentUrl: null, content: { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", type: "AdaptiveCard", version: "1.2", actions: [], body: [ { type: "TextBlock", text: $TITLE, color: $COLOR, eight: "bolder", size: "large", wrap: true }, { type: "TextBlock", text: $TEXT, color: $COLOR, size: "small", wrap: true } ] } } ] }' )" if [ "${BUTTON_URL:-}" != "" ] then JSON="$( echo "${JSON}" \ | jq \ --arg URL "${BUTTON_URL}" \ --arg TEXT "${BUTTON_TEXT:-URL}" \ '.attachments[0].content.actions += [ { type: "Action.OpenUrl", title: $TEXT, url: $URL } ] ' )" fi if [ "${EXPANDABLE_TEXT:-}" != "" ] then JSON="$( echo "${JSON}" \ | jq \ --arg TITLE "${EXPANDABLE_TITLE:-View more}" \ --arg TEXT "${EXPANDABLE_TEXT}" \ --arg URL "${EXPANDABLE_LOGO:-}" \ '.attachments[0].content.actions += [ { "type": "Action.ShowCard", "title": $TITLE, iconUrl: $URL, "card": { "type": "AdaptiveCard", "body": [ { type: "TextBlock", text: $TEXT, size: "small", wrap: true } ] } } ] ' )" fi if [ "${IMAGE_URL:-}" != "" ] then JSON="$( echo "${JSON}" \ | jq \ --arg IMAGE "${IMAGE_URL}" \ '.attachments[0].content.body = [ { type: "ColumnSet", columns: [ { type: "Column", items: .attachments[0].content.body }, { type: "Column", width: "auto", verticalContentAlignment: "center", items: [ { type: "Image", url: $IMAGE } ] } ] } ] ' )" fi # Post to Microsoft Teams using curl curl \ -v \ --header "Content-Type: application/json" \ --request POST \ --data "${JSON}" \ "${WEBHOOK_URL}"
-eight, +weight
@q2uantum You have to enable wrap with "wrap": true on the block you want the complete text. So for example the following
{"type": "TextBlock","wrap": true,"text": "${MESSAGE}"}
I haven't found anything (so far) to enlarge the Adaptive card.