WindowsとLinuxの両方で動くSlack通知用シェルスクリプトを書いてみました。

Table of Contents

はじめに

動作環境

以下の環境で動作することを確認済みです。

環境 バージョン
git bash(Windows 10 Home 10.0.18362) GNU bash, version 4.4.23(1)-release (x86_64-pc-msys)
bash (Ubuntu 18.04.2 LTS) GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)

前提

Incoming Webhooksのレガシーカスタムインテグレーション を使っています。

冒頭に記載のあるとおり、この方法は非推奨です。
しかし、通知先のchannelをパラメータで指定したかったから敢えてこれを使用しています。

非推奨な仕組みを使いたくない場合は

推奨される新しい方法を利用する場合は以下ページを参考にしてください。

その場合、Jsonのスキーマに若干変更があります。

実行

export SLACK_WEBHOOK_URL=https://hooks.slack.com/services/..............
./slack.sh "#your_channel" "スマイル一番"

こんな感じに通知されます。

実装

ソースコード

以下のようなslack.shを作成します。

#!/bin/bash

set -eu

post_slack() {
  # ex: "#times_hoge"
  channel=$1
  # ex: "hoge"
  user=$2
  # ex: "smile"
  emoji=$3
  # ex: "ほげほげ"
  message=${4//'"'/'\"'}
  # list: good / danger
  color=$5

  file=$(mktemp)
  trap 'rm ${file}' EXIT

  cat << EOJ > "$file"
    {
      "link_names": 1,
      "channel": "${channel}",
      "username": "${user}",
      "icon_emoji": ":${emoji}:",
      "attachments": [
        {
          "color": "${color}",
          "text": "${message}"
        }
      ]
    }
EOJ

  curl -s -S -X POST -d @"$file" "${SLACK_WEBHOOK_URL}"
}

post_slack "$1" smile_bot smile "$2" good
上記コードについて

ブログ執筆時のコードです。
最新のページをご覧下さい。

ポイント

実装にあたり、いくつかポイントがあります。

日本語のメッセージを送信できるようにする

Windowsの場合のみ日本語メッセージが送れない..という問題がありました。
以下はそのときのcurlコマンドです。

curl -s -S -X POST --data-urlencode 'payload={"link_names": 1, "channel": "'"${channel}"'", "username": "'"${user}"'", "icon_emoji": "'":${emoji}:"'", "attachments": [{"color": "'"${color}"'", "text": "'"${message}"'"}]}' ${SLACK_WEBHOOK_URL}

curl -X POST --data-urlencodeでマルチバイト文字をPOSTできます。
しかし、Windowsの場合はエンコーディングがCP932に強制されてしまいます。
SlackのAPIはUTF-8を要求するため、POSTしたJSONが正しくparseされずエラーになります。

解決策として、JSONをUTF-8エンコードした一時ファイルを作成し、curl --data-binary @{file}でファイルを指定するようにしました。

file=$(mktemp)

echo 'payload={"link_names": 1, "channel": "'"${channel}"'", "username": "'"${user}"'", "icon_emoji": "'":${emoji}:"'", "attachments": [{"color": "'"${color}"'", "text": "'"${message}"'"}]}' > "$file"

curl -s -S -X POST --data-binary @"$file" ${SLACK_WEBHOOK_URL}

JSONを読みやすくする

1行で書かれたJSONは読みにくいです。 curl --data-binaryの代わりにcurl -dを使うと改行の含むJSONをPOSTできます。

また、ヒアドキュメントを使うことで"'"といった複雑なコーテーションを排除しました。

file=$(mktemp)

cat << EOJ > "$file"
  {
    "link_names": 1,
    "channel": "${channel}",
    "username": "${user}",
    "icon_emoji": ":${emoji}:",
    "attachments": [
      {
        "color": "${color}",
        "text": "${message}"
      }
    ]
  }
EOJ

curl -s -S -X POST -d @"$file" ${SLACK_WEBHOOK_URL}

一時ファイルが残らないようにする

rm -rf ${file}を最後に実行すれば問題ないでしょうか?
途中でエラーが発生したり、強制終了されたら一時ファイルがゴミとして残ってしまいますね😅

mktempで作られるファイルパスはテンポラリー領域を指します。
放っておいても定期的に削除されるかもしれませんが、すぐ削除した方が安心です。

trapコマンドでEXITシグナルを受け取ったとき、ファイルを削除するようにしました。

file=$(mktemp)
trap 'rm ${file}' EXIT

cat << EOJ > "$file"
  {
    "link_names": 1,
    "channel": "${channel}",
    "username": "${user}",
    "icon_emoji": ":${emoji}:",
    "attachments": [
      {
        "color": "${color}",
        "text": "${message}"
      }
    ]
  }
EOJ

curl -s -S -X POST -d @"$file" ${SLACK_WEBHOOK_URL}

set -x slack.shでデバッグ出力すると、必ず最後にrm ${file}が実行されることを確認できます。

ダブルクォーテーションの考慮

messageにダブルクォーテーションを含むと、ivalid payloadエラーになります。
そのため変数代入時に"\"に置換して代入する必要があります。

  # ex: "ほげほげ"
  message=${4//'"'/'\"'}

総括

Windows(git bash)とLinux(bash)の両方で動くSlack通知用シェルスクリプトを紹介しました。
文字コードや使えるコマンドの制限など、Linuxだけのときに比べて気をつける必要があります。

しばらくの間は安心してSlack通知ができそうです😄