SlackのステータスをAPI経由で設定(例:現在iTunes再生中の曲を表示する)


Spotify版はこちら: https://qiita.com/keiya/items/06d40e67219e8b8bb841

準備1:Slackでアプリを作成

  • アプリをまず作成する。 https://api.slack.com/apps
    • チーム用のtokenが取得できるが、これは利用できない。
  • アプリの詳細設定"OAuth & Permissions"で、リダイレクトURLを http://localhost で設定
    • (実際に使うわけではないダミー)


準備2: OAuthで認証(自動化してない)

アプリのAuthorization Request && ユーザーのAuthorization Grant

  • 公式ドキュメント https://api.slack.com/docs/slack-button を見ながら、OAuthの一番最初のステップを行う
  • https://slack.com/oauth/authorize?scope=incoming-webhook,commands,bot,users.profile:write&client_id=<クライアントID> にブラウザでアクセスする
    • ローカルにWebサーバを立ててるなら止めておくこと
  • すると、ダミーの localhost にリダイレクトされる

  • リダイレクトされた先のクエリの code を取ってくる

アプリのAuthorization Grant && Access Token取得

slack-oauth.sh

  • 以下のスクリプトを実行する
oauth="https://slack.com/api/oauth.access"
client_id="nnn.nnn"
client_secret="asdf1234asdf1234"
code="nnnnn.nnnn.nnnxxxnn"
curl $oauth --data "client_id=${client_id}&client_secret=${client_secret}&code=${code}"
bash slack-oauth.sh
  • client_id と client_secret はアプリのページで取得
  • code はさっきの準備2で取ってきたやつ

  • このようにアクセストークンが取得できる

ステータスを設定するアプリを動かす

slack.sh

  • iTunesの現在の曲を取ってくる。
  • song の部分を変えれば曲以外にもつかえるかと
token="アクセストークンを設定"

rawurlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o

  for (( pos=0 ; pos<strlen ; pos++ )); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9] ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done
  echo "${encoded}"    # You can either set a return variable (FASTER) 
  REPLY="${encoded}"   #+or echo the result (EASIER)... or both... :p
}

song=$(osascript -e 'tell application "iTunes" to if player state is playing then name of current track & " - " & artist of current track')
json="{\"status_text\":\"${song}\",\"status_emoji\":\":musical_note:\"}"
profile=$(rawurlencode "$json")

curl https://slack.com/api/users.profile.set \
        --data "token=${token}&profile=${profile}"
bash slack.sh

自動で回すワンライナー

while true; do bash slack.sh; sleep 60 ; done

(追記) 機能強化バージョン

  • 自動でループを回す (コマンド単体で使えるようにした)
  • 1秒おきに取得するが、前回の曲と比較することにより曲が変わった時だけAPI問い合わせ
    • → 反応が早くなった(1秒以内)
    • APIの無駄遣いは無い
  • 曲停止時にステータスをクリアするようにした
token="アクセストークンを設定"

rawurlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o

  for (( pos=0 ; pos<strlen ; pos++ )); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9] ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done
  echo "${encoded}"    # You can either set a return variable (FASTER) 
  REPLY="${encoded}"   #+or echo the result (EASIER)... or both... :p
}

while true
do
  sleep 1
  song=$(osascript -e 'tell application "iTunes" to if player state is playing then name of current track & " - " & artist of current track')
  if [[ ${song} = ${lastsong} ]]; then
    continue
  fi
  status_emoji=":musical_note:"
  if [[ ${song} = "" ]]; then
    status_emoji=""
  fi
  json="{\"status_text\":\"${song}\",\"status_emoji\":\"${status_emoji}\"}"
  profile=$(rawurlencode "$json")

  curl https://slack.com/api/users.profile.set \
          --data "token=${token}&profile=${profile}"
  lastsong=$song
done
bash slack.sh

これはこのコマンドだけで良い。

バージョン2 (Unicode対応版)

  • Pure BashだけだとUnicodeのエンコードがおかしいのでPerlの助けを借りて日本語や多言語対応。
  • バージョン2.1(may 19 2017)
    • ダブルクォーテーションをスラッシュでエスケープするようにした
token="アクセストークンを設定"
while true
do
  sleep 1
  songraw=$(osascript -e 'tell application "iTunes" to if player state is playing then name of current track & " - " & artist of current track & " [" & album of current track & "]"')
  song=${songraw//\"/\\\"}

  if [[ "${song}" = "${lastsong}" ]]; then
    continue
  fi
  status_emoji=":musical_note:"
  if [[ ${song} = "" ]]; then
    status_emoji=""
  fi
  json="{\"status_text\":\"${song:0:100}\",\"status_emoji\":\"${status_emoji}\"}"
  profile="$(perl -MURI::Escape -e 'print uri_escape($ARGV[0]);' "${json}")"

  curl https://slack.com/api/users.profile.set \
          --data "token=${token}&profile=${profile}"
  lastsong=$song
done