backstage

合唱音源の新着情報の舞台裏

Twitter投稿をデータベースアプリケーションで自動化する

合唱音源の新着情報を運営してきて 真面目に分析 するにあたり、 1600件程度の自由形式なテキストデータを都度grepして分類・集計するのは限界だと思いました。

Twitter上のテキストデータをマスタとするのではなく構造化されたデータベースをマスタとして扱い、そこから集計をかけたりtwitterに投稿したくなりました。

要するに、合唱音源の新着情報専用のtwitterクライアントを作る事にしました。

要件

  • 投稿内容を「作曲者」「曲名」といった音源の属性にひも付けて集計できる
  • 予約投稿ができる

設計

分類や複雑な条件の検索に強いRDBMSを使い、どこからでも投稿できるようWebアプリケーションとします。

  • バックエンド画面からtweetの生成や予約投稿ができる
    • バックエンド画面にはログイン画面を設ける
  • フロントエンド画面でtweetを属性によって集計できる
  • できるだけMicrosoftの技術を用いる

使用技術

Microsoft Azure 仮想マシン

サーバにはMicrosoftが提供するIaaSである Azure 仮想マシン を使います。

この程度の用途ならVPSで十分?MicrosoftVPSがありますか?ありませんよね。

Ruby on Rails 4 (on Apache Passenger)

MicrosoftASP.NET(C#)と迷いましたが

という理由でRailsを採択しました。

なのでCSSプリプロセッサ(Sass)を使います。SassとSaaSって紛らわしい。

CentOS 6.5

さすがにIISRailsは修羅の道と判断しUNIXを選択。なおDockerみたいなオシャレな方法は使いません。OneNoteにサーバ構築の秘伝のタレが溜まっていきます。

ちなみに、AzureのPaaSを使ってRailsを動かす方法もあるようですが...

TypeScript

CoffeeScriptで十分?それはMicrosoftが作っていますか?作っていませんよね。

SQLite3

SQL Server?あーあー聞こえない

インフラの構築

まずAzureで最小構成のA0インスタンスを立ち上げ、 過去に書いた記事 と同様の方法でRailsのProduction環境を立ち上げます。

以下を見ながらWebサーバをWebrickApacheに変更。 (今回の用途ならばWebrickでも十分ですが…)

また、メモリが足りなくて後述のAssetCompileが落ちてしまったので、下記方法でSwap領域を作っておきます。 (AWS EC2もAzure VMも同じくSwap領域がありません)

アプリケーションの開発

DB設計は下記の通り、シンプルなm対n構造を用います。

f:id:s2terminal:20150215231433p:plain

下記のように、モデルクラスに has_manybelongs_to を記述するだけのシンプルな記法でこういったリレーションが利用できます。こういうのがActiveRecordを使いたかった理由です。

class Tweet < ActiveRecord::Base
  has_many :tweet_maps
  has_many :tweet_details, through: :tweet_maps
end
class TweetDetail < ActiveRecord::Base
  has_many :tweet_maps
  has_many :tweets, through: :tweet_maps
end
class TweetMap < ActiveRecord::Base
  belongs_to :tweet
  belongs_to :tweet_detail
end

↑こうするだけで、↓こういう事ができます。

<% tweet.tweet_details.each do |detail| %>
  <li><%= detail.value %></li>
<% end %>

RailsAdmin

バックエンド画面は少々作りこむ必要があるのですが、とりあえず最低限のものを自動で作ってくれるRailsAdminを導入。

rails_admin自体をカスタマイズしていく方法もあるようですが、下記のようにあまり使いやすいとは言えません。 また、hamlという謎の記法を覚えなければ容易に手を付けられないようです。

とりあえず、そのままにして使います。

twitter

twitter という名前そのまんまのgemがあり、こちらを用いると簡単にtwitter投稿機能を実装することができます。

RailsでTwitter botを作成 | EasyRamble

whenever

データベースに登録した内容を常に確認し、指定した時間に投稿するタスクスケジューラが必要です。 LinuxのcrontabをRailsで適当に取り扱うgemが whenever です。

これらを使い、

  1. whenever で投稿すべきデータが無いか毎分チェックする
  2. twitter で投稿する

これだけで、簡単なtwitter botが完成します。

# schedule.rb
set :environment, @environment
every '* * * * *' do
  rake "twitter:tweet"
end
# taskの中
tweet_data = Tweet.where("tweet_status = ? AND reserve_at < ?", 0, Time.now).order("created_at ASC").limit(1).first
next unless tweet_data
tweet = client.update(tweet_data.tweet_text.chomp)
tweet_data.update(:tweet_status => 10, :tweet_uri => tweet.uri.to_s)

当然のようにソースコード中にアクセストークン等すべてベタ書きなため一部抜粋です。よい子は真似しないこと。 先に挙げさせていただいたブログにもある通り、Rails4.1から実装されたsecret.ymlにでも書いてgitignoreするべきでしょう。

twitter文字数判定

twitterには文字数制限があり、プログラムでバリデーションをかけたいと思います。 twitterの文字数制限は140文字ですが、URLは短縮されたり何だかんだ色々なルールがあり、厳密にカウントするのは難しいです。 特に合唱音源の新着情報はURLを多く含むため、単純に length <= 140 等でバリデートしてしまうと片っ端から弾かれてしまい使い物になりません。

しかしそこはtwitter社、文字数判定などに使うプログラムを自らgithubに上げています。

twitter-text

これらを用いれば、正確な文字列判定などを実装できるそうです。まだ使っていませんが。

Railsバージョンアップ

開発途中でRailsのバージョンが上がっていったため、それに追従させました。

Ruby on Rails の大掃除! 3.2 → 4.1 にバージョンアップするの巻 - komiyakの通り道

上記を参考に、4.0.3から4.1.9にバージョンアップしました。 (4.2はrails_adminが上手く動かなさそうだったのでやめました)

手順自体は簡単で、Gemfileのバージョン番号を変えて bundle updaterake rails:update を打つだけです。 リリース前ならばやりたい放題です。

デプロイ

本プロジェクトはコスト削減のために本番サーバをリモートリポジトリにしております。すなわちrsynccapistranoのようなオシャレ技術は使うまでもなく、本番サーバに入り下記コマンドを黙々と実行すればデプロイ完了です。 シェルスクリプトにすらなっていない秘伝のタレです。

git rebase origin/master master
bundle install
rake db:migrate RAILS_ENV=production
rake assets:clobber
rake assets:precompile RAILS_ENV=production
service httpd graceful; service httpd status

先述のSwap領域を作っていないとprecompileで落ちる事があります。

リリース

wheneverでcronを仕込み、バックエンド画面から予約投稿を登録してしばし待ちます。 感動の瞬間です。

いつもと同じ投稿なのですが、こうして普通に表示され、普通にリツイートが来ているのを見ると幸せな気分になります。

なおこの方法で投稿した発言の特徴として、twiccaやTweetDeckなど一部のTwitterクライアントで見える使用クライアント名がAPI申請時に登録したアプリケーション名 「合唱音源の新着情報」 となります。

f:id:s2terminal:20150215191524p:plain

「TweetDeck」「TwitterWebClient」から「合唱音源の新着情報」に切り替わっています。

結論

たったこれだけの事に丸1年かかりました。

その間ずっとAzureのサーバ代も払っています。すしを2回たべられる程度のお金をMicrosoftに寄付しました。

感想としてはクリエイティブな箇所に時間を割くために、構築やデプロイに工数をかけたくありません。つまりAzureのPaaSでRailsが使いたいと思いました。それherokuで良いじゃん

参考文献