Skip to content

Instantly share code, notes, and snippets.

@lexisother
Created November 7, 2025 08:15
Show Gist options
  • Select an option

  • Save lexisother/6aa5cf62c87645d5a91dec9127e5e95e to your computer and use it in GitHub Desktop.

Select an option

Save lexisother/6aa5cf62c87645d5a91dec9127e5e95e to your computer and use it in GitHub Desktop.
Build Slack messages using shell scripts
#!/usr/bin/env bash
# Constants
readonly NETWORK_TIMEOUT=10
# Utils {{{
ansi_reset="$(tput sgr0 || true)"
ansi_red="$(tput setaf 1 || true)"
log_error() { echo >&2 "${ansi_red}[ERROR]${ansi_reset}" "$@"; }
# Use the log_error function to indicate where an error occurs
trap 'log_error line $LINENO' ERR
command_exists() { command -v "$1" &>/dev/null; }
curl() {
command curl --location --fail --max-time "$NETWORK_TIMEOUT" "$@"
}
function post_to_slack() {
curl -X POST -H "Content-Type: application/json" --data "$1" "$SLACK_WEBHOOK"
}
# JSON-specific
function build_json_array() {
local json="[]"
while (($# > 0)); do
if [[ "$1" == "" ]]; then
shift
continue
fi
json=$(jq -c --argjson item "$1" '. += [$item]' <<<"$json")
shift
done
echo "$json"
}
# }}}
# Preliminary checks {{{
if [ -z "$SLACK_WEBHOOK" ]; then
log_error "Variable \$SLACK_WEBHOOK wasn't found. Bailing!"
exit 1
fi
if ! command_exists jq; then
log_error "jq isn't installed. Bailing!"
exit 1
fi
# }}}
function create_divider() {
jq -n '{ type: "divider" }'
}
function create_text_object() {
local text="$1"
local type="${2:-mrkdwn}"
jq -n \
--arg text "$text" \
--arg type "$type" \
'{
type: $type,
text: $text
}'
}
function create_header() {
local text="$1"
local text_obj
text_obj=$(create_text_object "$text" "plain_text")
jq -n \
--argjson text "$text_obj" \
'{
type: "header",
text: $text
}'
}
function create_context() {
local text="$1"
local type="${2:-mrkdwn}"
local text_obj
text_obj=$(create_text_object "$text" "$type")
# this supports multiple elements (which will be placed horizontally next to
# eachother or under eachother depending on screen size) but i only really
# need one. adjust if required
elements=$(build_json_array "$text_obj")
jq -n \
--argjson elements "$elements" \
'{
type: "context",
elements: $elements
}'
}
function create_section() {
local text="${1:-null}"
local text_type="${2:-mrkdwn}"
local fields="${3:-null}"
local accessory="${4:-null}"
local text_obj
text_obj=null
if [[ "$text" != null ]]; then
text_obj=$(create_text_object "$text" "$text_type")
fi
jq -n \
--arg textval "$text" \
--argjson text "$text_obj" \
--argjson fields "$fields" \
--argjson accessory "$accessory" \
'{
type: "section",
}
+ if $text then {
text: $text
} else {} end
+ if $fields then {
fields: $fields
} else {} end
+ if $accessory then {
accessory: $accessory
} else {} end'
}
function create_action() {
local elements="$1"
jq -n \
--argjson elements "$elements" \
'{
type: "actions",
elements: $elements
}'
}
function create_button() {
local text="$1"
local style="${2:-}"
local url="${3:-}"
local text_obj
text_obj=$(create_text_object "$text" "plain_text")
jq -n \
--arg style "$style" \
--arg url "$url" \
--argjson text "$text_obj" \
'{
type: "button",
text: $text,
}
+ if $style != "" then {
style: $style
} else {} end
+ if $url != "" then {
url: $url
} else {} end'
}
function create_slack_payload() {
local sections_json="$1"
jq -n \
--argjson sections "$sections_json" \
'{
blocks: $sections
}'
}
# usage:
divider=$(create_divider)
header=$(create_header "I am a header")
ctx_line_1=$(create_context "Some context below header")
ctx_line_2=$(create_context "Some more context")
line_1=$(create_section "This is a paragraph")
button_1=$(create_button "Primary Button" "primary" "https://google.com")
button_2=$(create_button "Secondary Button" "" "https://google.com")
footer_actions=$(create_action "$(
build_json_array \
"$sandbox_button" \
"$pipeline_button"
)")
elements=$(
build_json_array \
"$header" \
"$ctx_line_1" \
"$ctx_line_2" \
"$divider" \
"$line_1" \
"$footer_actions"
)
# it's also possible to add elements conditionally:
elements=$(
build_json_array \
"$header" \
"$([ "$SOME_VAR" == "some value" ] && echo "$ctx_line_1" || echo "")"
)
payload=$(create_slack_payload "$elements")
if [[ "$DEBUG" == 1 ]]; then
echo "$payload" | jq .
else
post_to_slack "$payload"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment