青空文庫の作品を定期的に分割配信するTwitter bot @novels_botを作りました。
【人身御供募集】青空文庫の好きな作品を毎日少しずつ配信するtwitter bot @novels_botを作りました。まだまだ使いにくくまたデバッグの最中ですが試していただけたらうれしいです。@novels_botに話しかけると使い方が返信されます。よろしくお願いします。
気になっていて、何度も挑戦したけどどうしても読み切れない小説とかありませんか?私も含めてそんな人のために、本を一段落ずつ毎日配信するTwitter botを作りました。まだα版で色々問題あるかと存じますが、是非、お試しいただいて、ご意見、ご感想を教えていただけたら幸いです。
きっと知識と経験のある人なら1時間ぐらいでできる内容かと思いますが、なにぶん不慣れなため3日かかりました。以下、制作の際の備忘録です。
プラットフォーム
例によってherokuを利用させていただきました。言語はRubyです。今回、ウェブではないので、Rails/Sinatraは使いませんでした。(データベース管理のためSinatraのrake部分だけ利用しました。)
普段の言語がJavaScript/CoffeeScriptになっているので、Node.js系に移りたいと思っているのですが、プラットフォームを乗り換えるのは言語変えるより敷居が高いですね。
制約
無料で使える書籍としてとりあえず青空文庫のテキストフォーマットに限定しました。具体的には、日本語エンコードがシフトJISのzipファイルで、先頭行がタイトル、二行目が著者、その後、-------------…で表記コメントがくくられたファイルです。
細かい習得技術
Twitterのモニタリング
Twitter streaming APIは接続を維持するプッシュ型のAPIです。REST APIと違い回数制限がなく、ポーリングの必要もないので低負荷、低遅延のクライアントが設計できます。
Rubyではtweetstream gemが、twitter gem上に構築されていて開発アクティビティも高そうだったので、これを採用させていただきました。
日本語zipファイルの扱い
zipを扱うのに、zipruby gemを使いました。(rubyzipはJava的APIという噂だったので。)zipファイルをブロック付けてopenすると、引数にar(Archive?)が渡されてこれにファイル名を渡してfopenすると中のファイルが読めます。
Ruby 1.9系ではopenする時にオプションで"r:Shift_JIS"という感じにエンコードを指定すると、その日本語エンコードでファイルを読んだ後、スクリプトのエンコードに変換してくれます。ところが、上記のfopenではこれが使えませんでした。
なので、ファイルを全部読み込んだ後、ライブラリのkconvを使ってtoutf8で8ビット文字列をUTF-8に変換しました。
Railsなしでデータベースをどう使うか
ActiveRecordに少しは慣れているというかそれ以外には何も知らないので、ActiveRecordを使うことにしました。
herokuでは、Railsでない場合、Heroku Postgresというアドオンを用意してくれています。(以前はshared-databaseというアドオンがありましたが、これが置き換わったようです。)heroku createを実行したディレクトリで、
heroku addons:add heroku-postgresql
を実行すると、生成されたデータベース名(HEROKU_POSTGRESQL_XXXX)が通知され、アプリケーションで利用可能になります。データベース名は管理の際に必要です。
migrationするにもアプリで使うにもデータベースに接続する必要があります。herokuはconfig/database.ymlをユーザーが用意したgitの内容とは別に生成するので、データベースに接続する方法はローカルとherokuと異なります。四苦八苦して以下のようになりました。
if ENV['RAILS_ENV'] == 'production' require 'uri' db = URI.parse(ENV['DATABASE_URL'] || 'postgres://localhost/mydb') ActiveRecord::Base.establish_connection( :adapter => db.scheme == 'postgres' ? 'postgresql' : db.scheme, :host => db.host, :port => db.port, :username => db.user, :password => db.password, :database => db.path[1..-1], :encoding => 'utf8' ) else require 'yaml' ActiveRecord::Base.establish_connection(YAML::load(File.open('config/database.yml'))[ENV['RAILS_ENV']]) end
RAILS_ENVなんて引きずったりして恥ずかしいです。直す予定です。
migrationについてはあれこれ検索してrequire 'sinatra/activerecord/rake'を使いました。本質的には、SinatraのFAQの内容なので、gemでsinatra-activerecordをbundleするより直接Rakefileに記述した方が筋がよかったと思っています。これも直す予定です。
Heroku Scheduler
https://devcenter.heroku.com/articles/scheduler
Heroku Schdulerはcronの代わりとなるアドオンです。コンソールから起動するコマンドを作って、ウェブでスケジュールを登録すると設定に合わせて定期的に実行されます。これで配信コマンドを定期的に実行しています。
heroku-postgresqlのアドオンの際には必要なかったのですが、このアドオンをインストールするにはクレジットカード登録が必要でした。(このアドオン自体は無料です。)
苦労したところ
herokuでのデータベースのmigrate
bundleした各gemのバージョンとローカルのgemのバージョンに不一致があって、rake db:migrateでうまく行くのにbundle exec db:migrateはうまく行かない状態でした。これをherokuでの問題と勘違いして、「herokuでdb:migrateできない」とえらく時間を取りました。
感想
プログラムの仕様をイメージした瞬間、実装可能なことは容易にわかるので、システムを使うための苦労がストレスでした。3日で済んでよかったです。ドツボにはまって4日、5日と経ったら投げ出していたと思います。慣れている人なら1〜2時間程度の内容でしょうか。
実装できたお陰で、色々拡張も思いつきます。英語化Gutenberg拡張とかタイトルからのURL検索機能とか、配信をさかのぼる「戻る」機能とか共有購読とか。評判が悪くなければぼちぼち拡張したいと思います。