【第二回】Rubyで作ったSlack BotとSpotify APIで遊んでみた

この記事はCAMPHOR- Advent Calendar 2016の17日目の記事です

謝罪

いきなり謝罪です。Advent Calendar上で予告していたタイトル「Rubyで作ったSlack BotとRhymerで遊んでみた」ですが、大嘘であることをここに謝罪します。ごめんなさい。

もともとは今年の中頃に一部界隈をおおいに賑わせたRhymerを使って遊ぼうと思っていたのですが、諸般の事情(スベりそう)から止むを得ず内容を変更させていただきました。興味のある方は以下のリンクより Rhymerをお楽しみください↓↓

qiita.com

ごあいさつ

先日の記事は多くの方に読んでいただけたようで非常に嬉しく思います。本日は予告通り2本立ての2本目にあたる内容です。

みなさん、Spotify使ってますか?

Subscription系の音楽サービスとしては近年「Apple Music」「AWA」「LINE Music」など多くのプレイヤーが参入してきましたが、特に洋楽好きの方などはまだまだ「Spotify」人気が根強いのではないでしょうか。今回はみんな大好きSpotifyAPIを使ってSlack Botを強化していきたいと思います。

概要

  • Bot本体は前回の記事で使用したファイルをそのまま使ってます
  • bgmというコマンドを入力することでSlackのタイムライン上にSpotifyの埋め込みリンクを表示するBotを作ります
  • 楽曲、アーティスト、アルバム、プレイリストのそれぞれで検索機能を実装
  • ジャンルをもとにしたレコメンド機能を実装
  • より複雑な機能を実装しようとおもったけど断念(後述)
  • 正規表現の理解度がダメダメなのでツッコミお待ちしています

目次

  1. RSpotify
  2. 検索機能の実装
  3. レコメンド機能の実装
  4. 個人利用のためのカスタマイズ(断念)

環境

  • macOS Sierra 10.12.2 Beta
  • MacBook Air (13-inch, Early 2014)
  • ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]

参考

やっていくぞ!!

1. RSpotify

RSpotifyGuilherme Sad氏によるSpotify Web APIRubyラッパーです

1.1. 開発用キーの設定

Spotify Developer Pageから新規アプリケーションを作成し、 Client IDClient Secretの2つのトークンを取得します。

f:id:andoshin11:20161216070927p:plain

f:id:andoshin11:20161216071103p:plain

トークンを取得したらそれぞれ SPOTIFY_CLIENT_IDおよび SPOTIFY_CLIENT_SECRETという環境変数として保存します。方法は前回の記事を参考にしてください。

1.2. RSpotifyのインストール

前回作成した Gemfileに追記

# Gemfile
source 'https://rubygems.org'
ruby "2.3.1"

gem 'http'
gem 'json'
gem 'faye-websocket'
gem 'eventmachine'
gem 'rspotify'

$ bundleコマンドでGemをインストール f:id:andoshin11:20161216051148p:plain

1.3. RSpotifyの動作確認

RSpotifyと関連するGemのインストールが完了したら動作をテストしてみましょう。前回同様、 bot.rbに追記していきます。

# bot.rb
require 'http'
require 'json'
require 'eventmachine'
require 'faye/websocket'
require 'rspotify' # RSpotifyの読み込み

# アプリの認証情報
RSpotify.authenticate(ENV['SPOTIFY_CLIENT_ID'], ENV['SPOTIFY_CLIENT_SECRET'])

(省略)

  ws.on :message do |event|
    data = JSON.parse(event.data)

    # data['text']が存在しなければスキップし、存在すればmsg変数に代入
    next if data['text'].nil?
    msg = data['text']

    p [:message, data]

    if msg == 'こんばんは'
      ws.send({
        type: 'message',
        text: "こんばんは <@#{data['user']}> さん"+data['text'].split(" ").first,
        channel: data['channel']
        }.to_json)
    end

    # "bgm"という単語から始まるテキストが入力された時の処理
    if msg.match(/^bgm/)
      test_url = RSpotify::Track.find('5CwhBU7eqQgkOVMnRNuzzT').external_urls['spotify']
      ws.send({
        type: 'message',
        text: test_url,
        channel: data['channel']
        }.to_json)
    end
  end

(省略)

bgmという単語から始まるテキストがSlackに投稿された時に Spotifyから 5CwhBU7eqQgkOVMnRNuzzTというIDに該当する曲を探し、その埋め込み用リンクを返すような処理です。

このコードを実行してみましょう。 f:id:andoshin11:20161216053008p:plain

https://gyazo.com/d978f4f1449f7ab0e7f33db822a1ab1f.gif

ID: 5CwhBU7eqQgkOVMnRNuzzTの曲、つまりGet Wildの埋め込みリンクが無事タイムラインに投稿されました!

2. 検索機能

2.1. 楽曲を検索できるようにする

無限にGet Wildを聴き続けるのもよいですが、せっかくなら楽曲を検索して表示したいものです。

bgm song (クエリ)というテキストが入力されたらクエリに該当する楽曲を検索

# bot.rb
...

    if msg.match(/^bgm/)
      text = "No result"
      if matched_msg = msg.match(/^bgm song (.*)/)
        track = RSpotify::Track.search(matched_msg[1]).first
        text = track.external_urls['spotify'] unless track.nil?
      end
      ws.send({
        type: 'message',
        text: text,
        channel: data['channel']
        }.to_json)
    end
...

実行

https://gyazo.com/4bbcd364454951083817bb7f1f97e580.gif

2.2 アーティスト、アルバム、プレイリストの検索機能

上記のコードと同じ要領でアーティストやアルバム、プレイリストについても検索機能を実装していきます

# bot.rb

...
    if msg.match(/^bgm/)
      text = "No result"
      if matched_msg = msg.match(/^bgm song (.*)/)
        track = RSpotify::Track.search(matched_msg[1]).first
        text = track.external_urls['spotify'] unless track.nil?
      elsif matched_msg = msg.match(/^bgm artist (.*)/)
        artist = RSpotify::Artist.search(matched_msg[1]).first
        text = artist.external_urls['spotify'] unless artist.nil?
      elsif matched_msg = msg.match(/^bgm album (.*)/)
        album = RSpotify::Album.search(matched_msg[1]).first
        text = album.external_urls['spotify'] unless album.nil?
      elsif matched_msg = msg.match(/^bgm playlist (.*)/)
        playlist = RSpotify::Playlist.search(matched_msg[1]).first
        text = playlist.external_urls['spotify'] unless playlist.nil?
      end
      ws.send({
        type: 'message',
        text: text,
        channel: data['channel']
        }.to_json)
    end
...

実行!

https://gyazo.com/1f7ad569f004bab3d9c34ca81085536f.gif

https://gyazo.com/c46fa903e6348523f8463957e55ad0a2.gif

YOSAGE(ガッツポーズの絵文字)

3. レコメンド機能

音楽のジャンルごとに楽曲をレコメンドしてくれる機能を実装します。 bot.rbに追記

# bot.rb
...

    if msg.match(/^bgm/)
      text = "No result"
      if matched_msg = msg.match(/^bgm song (.*)/)
        track = RSpotify::Track.search(matched_msg[1]).first
        text = track.external_urls['spotify'] unless track.nil?
      elsif matched_msg = msg.match(/^bgm artist (.*)/)
        artist = RSpotify::Artist.search(matched_msg[1]).first
        text = artist.external_urls['spotify'] unless artist.nil?
      elsif matched_msg = msg.match(/^bgm album (.*)/)
        album = RSpotify::Album.search(matched_msg[1]).first
        text = album.external_urls['spotify'] unless album.nil?
      elsif matched_msg = msg.match(/^bgm playlist (.*)/)
        playlist = RSpotify::Playlist.search(matched_msg[1]).first
        text = playlist.external_urls['spotify'] unless playlist.nil?

      # 追記部分
      elsif matched_msg = msg.match(/^bgm recommend (.*)/)
        result = RSpotify::Recommendations.generate(seed_genres: [matched_msg[1]]).tracks.sample
        text = result.external_urls['spotify'] unless result.nil?
      end

      ws.send({
        type: 'message',
        text: text,
        channel: data['channel']
        }.to_json)
    end
...

bot recommendから始まる入力が与えられたときに RSpotify::Recommendations.generateを実行しています。 RSpotify::Recommendations.generateからはおすすめの track20曲分(デフォルト時)の配列が得られるため、その中からランダムで1曲を抜き出して埋め込みリンクを表示するような処理です。

レコメンド機能を試してみましょう。

https://gyazo.com/9f6a6eb6f3641eb62b28b76173c9fa8d.gif

こちらもYOSAGEですね。ちなみに2016年12月16日現在レコメンド機能がカバーしているジャンルは以下の通りです

  {
  "genres" : [ "acoustic", "afrobeat", "alt-rock", "alternative", "ambient", "anime", "black-metal", "bluegrass", "blues", "bossanova", "brazil", "breakbeat", "british", "cantopop", "chicago-house", "children", "chill", "classical", "club", "comedy", "country", "dance", "dancehall", "death-metal", "deep-house", "detroit-techno", "disco", "disney", "drum-and-bass", "dub", "dubstep", "edm", "electro", "electronic", "emo", "folk", "forro", "french", "funk", "garage", "german", "gospel", "goth", "grindcore", "groove", "grunge", "guitar", "happy", "hard-rock", "hardcore", "hardstyle", "heavy-metal", "hip-hop", "holidays", "honky-tonk", "house", "idm", "indian", "indie", "indie-pop", "industrial", "iranian", "j-dance", "j-idol", "j-pop", "j-rock", "jazz", "k-pop", "kids", "latin", "latino", "malay", "mandopop", "metal", "metal-misc", "metalcore", "minimal-techno", "movies", "mpb", "new-age", "new-release", "opera", "pagode", "party", "philippines-opm", "piano", "pop", "pop-film", "post-dubstep", "power-pop", "progressive-house", "psych-rock", "punk", "punk-rock", "r-n-b", "rainy-day", "reggae", "reggaeton", "road-trip", "rock", "rock-n-roll", "rockabilly", "romance", "sad", "salsa", "samba", "sertanejo", "show-tunes", "singer-songwriter", "ska", "sleep", "songwriter", "soul", "soundtracks", "spanish", "study", "summer", "swedish", "synth-pop", "tango", "techno", "trance", "trip-hop", "turkish", "work-out", "world-music" ]
}

4. 個人利用のためのカスタマイズ(...だがしかし断念)

当初の予定ではここからユーザー情報で OAuth認証してSlackから個人のプレイリストに曲追加したりいろいろやりたいことがあったけど、Advent Calendar担当日のタイムリミットが来たので断念...

近いうちにリベンジしたいです。

おわりに

日本でのサービスイン以前からSpotifyを利用しているヘビーユーザーなのでAPIを叩くだけで遊べるのはとてもありがたいです。 Slack Botに限らず今後もいろんな機会で使っていきたいと思います。

明日はmurata_atsumiの記事です

Let's Get Wild!

open.spotify.com