人気のTwitterクライアントTweetDeckにFoursquareのチェックイン履歴を表示してみました。
きっかけ
なんでこんなことやってるのかというと下記ブログでFoursquareのAPIの存在を知り、せっかくなので自分もやってみたかったのです。
- FourSquare API を Node.js から使ってみる (前編) - 自習室
- 今までfoursquareにチェックインした都道府県を塗りつぶしてくれるツールを作ったよ - koogawa blog
割りとTwitter廃人のため、オリジナルのWebアプリを作るよりはよく使うTweetDeckのカラムに友人や自分のチェックイン情報が流れてきたら面白いんじゃね?と思ったのがきっかけです。
最初にネタバレするとこれはできませんでした(え)。が、FoursquareやChrome 拡張を使ってそこそこ時間がかかったので記事にします。
構成
TweetDeckのWebページに対してChrome Extensionsを使ってHTMLの改ざんを行います。上の画像で分かる通りカラムを真似てFoursquareのチェックイン情報をアイテムとして表示しています。
Chrome ExtensionsはChrome拡張のことでHTMLとJavascriptの知識があれば簡単に作れる便利なものです。
foursquare API
TwitterなどのOAuthによるアプリを作ったことがある人はあまり苦労しないと思います。まずはアプリを作り、Client IDなどを生成しましょう。
次にこのアプリを使うユーザーに権限を委譲してもらいアクセストークンを生成してもらいます。下記のToken Flowの1と2を実行しましょう。
アクセストークンができたら試しに権限を委譲してもらったアカウントのチェックインリストをjsonでもらいます。vはバージョニングでこの日付のfoursquareのAPI仕様を利用するというものです。afterTimestampはこの日付以降のもの、ということです。これはUNIXエポックと呼ばれる時間形式で1970年1月1日からの経過時間を示しています。オンラインで見やすい形式に変換するものもあります。テストではcurlでアクセスしてますが、もちろんブラウザで直接開いてもOKです。
$curl https://api.foursquare.com/v2/users/self/checkins?oauth_token=YOUR_ACCESS_TOKEN&v=20140212&afterTimestamp=1279044824
ここで友人のチェックイン情報もuser_idを指定して取得しようとしたところnot-authorizedとでてしまいました。どうやらこのAPIではまだUSER_IDはselfしか対応してないとのこと。がっくり。友人のリストは取得できます。(後述)
jsonのデータ構造ですが、下記のようにきれいに整形してくれるサービスを利用すると目的の値にどのようにたどっていけばわかりやすいです。
manifest.json
Chrome extensionsで使うmanifest.jsonになります。Chrome拡張でやらないよ、という人は見なくてもOKです。contentscript.jsが今回のメインのJavaScriptになります。
{ "name": "DeckSquare", "version": "1.0", "manifest_version": 2, "description": "DeckSquare", "content_scripts": [ { "run_at": "document_end", "matches": ["http://*/*", "https://*/*"], "js": ["js/jquery-1.7.2.min.js", "js/contentscript.js"] } ], "permissions": [ "tabs", "http://*/", "https://*/", ] }
contentscript.js
今回のメインコードになります。開いたページのタイトルがTweetDeckであった場合、チェックインリストを受け取って無理やりTweetDeckに突っ込んでます。データを取得するところまではいいと思いますが、やはり無理やり突っ込むのは無理がありそうですね…。自分でcssを書けば良いのですが、今回は面倒なのでTweetDeckのクラスなどをそのまま使わせてもらいました。汚くて読めたものではないですね。
var fromDay = 7; var today = new Date(); var afterTimestamp = today.setDate(today.getDate() - fromDay) - today.getTimezoneOffset() * 60 * 1000; if (document.title.indexOf('TweetDeck') != -1) { window.setTimeout('GetCheckinData()', 3000); } function GetCheckinData() { $.getJSON( 'https://api.foursquare.com/v2/users/self/checkins?', { 'oauth_token': 'YOUR_ACCESS_TOKEN', 'v': '20140212', 'afterTimestamp': afterTimestamp }, function(data, status) { if(status === 'success') { var checkins = data.response.checkins.items; InsertFoursquareDate(checkins); } } ); } function InsertFoursquareDate(checkins) { var columnParent = $('.js-app-columns.app-columns.horizontal-flow-container.without-tweet-drag-handles'); var foursquareColumn = $('<section>', {class: 'js-column column column-type-activity will-animate'}) .append($('<div>', {class: 'js-column-holder column-holder'}).add($('<div>', {class: 'column-panel'}))); var contentContainer = $('<div>', {class: 'js-column-content column-content'}); checkins.forEach(function(ck) { var venue = ck.venue; // convert epoch time to local time // a parameter for Date class is milliseconds. // createdAt[second], timezoneoffset[minute] var date = new Date((ck.createdAt + ck.timeZoneOffset * 60 ) * 1000).toLocaleString(); var icon = venue.categories[0].icon; var iconUri = icon.prefix + '64' + icon.suffix; var article = $('<article>', {class: 'stream-item js-stream-item is-actionable'}) .append($('<div>', {class: 'js-stream-item-content item-box js-show-detail'})); var item = $('<div>', {class: 'js-tweet tweet'}); var iconContainer = $('<div>', {class: 'obj-left item-img tweet-img', width: 48, height: 48}); // must set icon size. 32, 44, 64, and 88 are available var mapUrl = 'http://maps.google.co.jp/maps?q=' + encodeURI(venue.name) + '&=ll' + venue.location.lat + ',' + venue.location.lng + '&hl=ja'; var mapDom = $('<p>').append($('<a>', {'href': mapUrl, 'text': 'Open Google map'})); var iconDom = $('<img/>', {class: 'tweet-avatar avatar pull-right', 'src': iconUri}); iconContainer.append(iconDom); item.append(iconContainer); item.append($('<font>', {text: date, color: '#999999'})).append($('<br>')); item.append($('<font>', {text: venue.name})); item.append(mapDom); article.append(item); contentContainer.append(article); }); foursquareColumn.append(contentContainer); columnParent.prepend(foursquareColumn); }
最後に
本当はチェックインの通知もアプリで取得できるのですが、TweetDeckの改ざんが想定よりも筋が悪い気がしてきたので、このタスクは一旦止めちゃいました。ただ通知のフックやグローバルのストリーミングデータ(別途foursquareチームにメールが必要)もあるようなので、また別ネタでリトライ予定です。Chrome Extensionsで通知だけ、などでも良いかもしれないですね。
おまけ
ちなみに今回は使いませんでしたが、自分の友人の情報を取得するにはこんな感じでできました。
$.getJSON( 'https://api.foursquare.com/v2/users/self/friends?', { 'oauth_token': 'YOUR_ACCESS_TOKEN', 'v': '20140212', 'afterTimestamp': '1279044824' }, function(data, status) { if(status === 'success') { var friendsData = data.response.friends.items; friendsData.forEach(function(friend) { var id = friend.id; var firstName = friend.firstName === undefined ? '' : friend.firstName; var lastName = friend.lastName === undefined ? '' : friend.lastName; var name = firstName + lastName; console.log(id + ': ' + name); }); } } );