susunshunのお粗末な記録

お粗末に丁寧に生きる

herokuを用いたbotの常時運用について

やあ(´・ω・`)

勉強会で発表したものの、ブログにおこしてないトピックが何個かあるので書いてくよ。

botの常時運用

以前evernoteノートの記事をランダムにチョイスしてつぶやくbotを作りました。(記事は後日書きます)

botを常時稼働させる環境として、以前はherokuが一強でしたが
ある日を境に無料枠では18時間/日という制限がついてしまいました。。。
(残念ですが、むしろ無料というのが虫のいい話ですもんね)

AWSでEC2立てちゃう等々、やりようはありますが、
ここではあくまでも「herokuで」、「無料枠で」、運用する方法を紹介します。

といってもかなりごり押し感満載なので参考までに。。。

herokuの制約

1時間以上アクセスがないとsleepする

じゃあ1時間に1回アクセスすればいいじゃん、ということでheroku schedulerというアドオンを使います。
elements.heroku.com

f:id:susunshun:20160522231948p:plain

これでherokuにアクセスするコマンド(curl)を毎時実行してsleepを防ぐわけです。
f:id:susunshun:20160522231839p:plain

18時間/日しか動かせない

じゃあ18時間以内に抑えればいいじゃんということで、今度はprocess schedulerというアドオンを使います。
elements.heroku.com


f:id:susunshun:20160522232315p:plain

これでアプリを二つデプロイして、各々以下の設定をします。
f:id:susunshun:20160522232623p:plain

アプリを二つデプロイするところがだいぶごり押しですが、めでたく無料枠で常時運用が可能です!
やったね!

余談

evernoteのリマインドbotは日中帯に動いてくれればいいので、普通に18時間の制限内で稼働させてます。
他のbotも夜な夜な稼働し続けたいものは少ないと思うので、どっちかというと情報収集系のバッチを動かしたり、なんてことに使うかもしれませんね。

いま溜まっているトピックとしては

  • 終電一発検索アプリを作った
  • 読書管理アプリを作った
  • monaca開発の是非を問う
  • evernoteからノートをランダムに取得してbotでリマインドする

あたりですね。ちょくちょく記事におこしていきます。

Ionicに興味深々なこの頃。AngularJS2も触ってみなければ。。。

社内ハッカソンでプレゼンがんばった話

半年前の社内ハッカソンでは結果は20数チーム中7位と、なんともいえない結果でした。。。

susunshun.hatenablog.com

あれから半年、またハッカソンが開催されたので、振り返り用として反省点、やったことを書きます。
プレゼンをがんばったのでアプリの話ではなく、プレゼンの話が主です(。・ω・。)

前回の反省

デモ

前回はアイデア的には悪くない、アプリの完成度も高かった、と思うのですがデモで失敗しました。。。(ノ_-。)
冷蔵庫の在庫をタグ管理するアプリを作ったものの、デモでは読み取り端末にタグをピッとタッチしただけ。
聞いてる側はそのアプリがどういう風に使われるのかイメージが難しかったとおもいます。

こまけぇことはいいんだよ!

ハッカソン共通で言えることですが、

  • プレゼンが短い(今回は4分or5分)
  • 発表チームが多い

ということがあり、あまり難しい内容をつらつらと発表してもおじさん達の頭には入っていきません。

前回はアプリ概要からビジネスモデル、技術要素とてんこ盛りの5分にした結果、
結局アプリの魅力が伝わりきらなかった気がします。

つまり

f:id:susunshun:20160214163652j:plain

こういうことです。

評価基準を履き違えていた

作ったアプリそのものが評価される、つまりアプリの完成度=評価という前提のもとでアプリ本体を作りこみましたが、
そこはやはりSIerの社内ハッカソン、ビジネスモデルやサービスコンセプトが明確なチームの方が評価されていた気がします(*´ρ`*)

反省まとめ

イデアの良さ、技術レベルの高さ、よりも『見せ方』、これに尽きるという結論に至りました!
※もちろんある程度のアイデアの良さ・技術レベルに到達してることが前提となります

今年のハッカソン

こんなものを作ったよ

対象物をアプリで撮影すると、自分と属性が似ているユーザのクチコミを引っ張ってきてくれる
というアプリを作りました。

f:id:susunshun:20160214165215j:plain
f:id:susunshun:20160214165238j:plain

デバイスを使ったアイデアを!

やはりWebで完結したアプリよりも、なんかしらのデバイスを使った方がデモが映えます。
作ってても楽しいです。
ということもあり今回はスマホのカメラを使いました。

PVを入れる

聞き手にイメージを持ってもらうためにプレゼン中のデモに加えてプロモーションムービーを作成しました。
撮影のためにハッカソン中に本屋へ行ったりwww
ムービー作成は他のメンバーに任せたのですがガチクオリティのPVを作ってくれてマジ感謝ヾ(○´□`○)ノ"

インパクトがあるプレゼンよりも理解されるプレゼン

最近の流行りとして、スライドいっぱいに写真を表示して一言だけ文字が書いてある、というものがあります。
こういうのです。

www.slideshare.net

今回のハッカソンでも何チームかがそのようなスライドでした。

オシャレですしで個人的には好きなのですが、この方法は短時間で聞き手に「理解させる」というよりも「インパクトを残す」、という点に利があると思っています。
発表するチーム数が多いので、なんとなくイメージには残ってるけど結局何だったっけ?ということになりかねません。

今回は「インパクトを残す」役割はPVに任せ、スライドでは「理解させる」ことが重要と捉え作成しました。

f:id:susunshun:20160214180023j:plain

↓こういう点はいつも心がけるようにしています。

www.slideshare.net

予選と本選でプレゼンの構成を変える

今回実は予選と本選にわかれており、プレゼンの時間や審査員が異なっています
そこで予選と本選でプレゼンの構成を変えました。

◼︎予選(20チームくらい)
時間:4分
審査員:参加者、ハッカソン運営
プレゼン構成:
問題提起

解決のアプローチ

アプリ説明

デモ

PV(short ver.)

技術要素

とにかく時間がないのでPVはデモの補足説明的なポジションでデモの直後に入れています

◼︎本選(予選から選抜された6チーム)
時間:5分
審査員:社長、役員、スポンサー企業様
プレゼン構成:
★PV(full ver.)

問題提起

解決のアプローチ説明

アプリ説明

デモ

技術要素

★締めの言葉的なフリートーク

コンセプトやイメージを最初に刷り込むためにPVを先頭に持ってきています。
最後には締めの言葉的なことを言うアドリブタイムを設けました。

時間が足りず最後の方は早口、挙句の果てにタイムアップで途中で切られる、というチームが続出する中、
うちのチームは時間通りで余裕を持ってプレゼンを締めることができたので有効な策でした。

スライドとアプリのデザインを統一する

スライドに違和感を出さないために、スライドとアプリのデザインや配色は統一しています。

ビジネスモデルはプレゼンから省いて質疑で受ける

プレゼンは前述の通り5分しかないので、ビジネスモデルの説明は完全にカットしました。
説明しなけりゃ逆に質疑応答タイムで聞いてくるだろうと予測して、発表外のスライドとして用意。

結果は。。。!?

5つの賞のうち2つを受賞することができました!
最優秀賞は逃したものの、ダブル受賞→賞金王です。

プレゼンがどこまで評価に寄与したかは分からないですが、確実に加点要素にはなっていたはずです。

イデアは浮かぶか浮かばないか、不確実性がありますが、プレゼンはポイントを押さえていれば誰でもクオリティを上げられると思うので、ハッカソンといえど今後も力を入れておきたいところです。

また、今回は同期(3年目)4人でチームを組んで参加しました。
若い奴らが社内の大きなイベントで目立つということは会社的にもとても良いことだと思うので、今後も受賞狙ってプレゼン頑張ります('∇'*)

hubot導入めも (インストールからherokuアップロードまで)

f:id:susunshun:20151030005737p:plain

hubot(というかbot)が面白そうだということで触ってみた。

hubotってなにさ?

  • Node.jsでbotを作り動かすためのフレームワーク
  • え?Node.js?CoffeScript?(聞いたことあるけど書いたことない)
  • けど面白そうだから触ってみたい

botを作るだけだったらフレームワークなんて使わんでも簡単にできるよ、twitterbotツールとかあるし」
そう思った方、正にそうです。

ただhubotは様々なチャットツールに対応しています。
hubotとチャットツールをつなぐ役目を担うモジュール「Adapter」を切り替えることで、
botの処理を1回書くだけで様々なチャットツールに対応させることができます。

イメージこんなん(勉強会で使った資料から抜粋)
f:id:susunshun:20151030010538p:plain

やること

  1. Node.jsインストール
  2. hubotインストール
  3. hubot起動
  4. adapterインストール
  5. herokuデプロイ
  6. (おまけ)Cronで動かす

ちなみに今回いろいろと調べながら導入作業をしていたのですが、
この記事が全般的にとても参考になりました。マジ感謝。gihyo.jp

Node.jsインストール

node.jsのバージョン管理をしやすくするためにNodebrewをインストールします。

$ brew update
$ brew install nodebrew

インストールの過程でパスを通してね、というメッセージが出ます。

Add path:
  export PATH=$HOME/.nodebrew/current/bin:$PATH

To use Homebrew's directories rather than ~/.nodebrew add to your profile:
  export NODEBREW_ROOT=/usr/local/var/nodebrew


ということでパスを追加(bashの場合)

$ vi ~/.bash_profile
$ source ~/.bash_profile


nodebrewを最新版に更新

$ nodebrew install latest


使用するバージョンを指定する場合には以下

$ nodebrew use v4.2.1
$ node -v
v4.2.1

hubotインストール

ではではhubotをインストールしましょう( ´ ω ` )

$ sudo npm install -g hubot coffee-script


hubotを召喚します

$ hubot --create myhubot


なんかでた

'hubot --create' is deprecated. Use the yeoman generator instead:
    npm install -g yo generator-hubot
    mkdir -p myhubot
    cd myhubot
    yo hubot
See https://github.com/github/hubot/blob/master/docs/index.md for more details on getting started.

どうやら「createはもう古いぜyeomanを使えよfuck」ってことらしいですqiita.com

ということでそのままコマンドを・・・叩きこむっ!

$ npm install -g yo generator-hubot
$ mkdir -p myhubot
$ cd myhubot
$ yo hubot

召喚成功するとヤツが登場します。

                     _____________________________
                    /                             \
   //\              |        fuXX  you.            |
  ////\    _____    |      What’s up, man ?        |
 //////\  /_____\   \                             /
 ======= |[^_/\_]|   /----------------------------
  |   | _|___@@__|__
  +===+/  ///     \_\
   | |_\ /// HUBOT/\\
   |___/\//      /  \\
         \      /   +---+
          \____/    |   |
           | //|    +===+
            \//      |xx|
※一部改変あり

hubot起動

hubotを起動してデフォルトで備わっているコマンドを試してみる

$ ./bin/hubot

するとhubotと対話モードになります。
コマンド一覧を表示する

myhubot>myhubot help

myhubot animate me <query> - The same thing as `image me`, except adds a few parameters to try to return an animated GIF instead.
myhubot echo <text> - Reply back with <text>
myhubot help - Displays all of the help commands that myhubot knows about.
myhubot help <query> - Displays all help commands that match <query>.
myhubot image me <query> - The Original. Queries Google Images for <query> and returns a random top result.
myhubot map me <query> - Returns a map view of the area returned by `query`.
myhubot mustache me <query> - Searches Google Images for the specified query and mustaches it.
myhubot mustache me <url> - Adds a mustache to the specified URL.
myhubot ping - Reply with pong
myhubot pug bomb N - get N pugs
myhubot pug me - Receive a pug
myhubot the rules - Make sure myhubot still knows the rules.
myhubot time - Reply with current time
myhubot translate me <phrase> - Searches for a translation for the <phrase> and then prints that bad boy out.
myhubot translate me from <source> into <target> <phrase> - Translates <phrase> from <source> into <target>. Both <source> and <target> are optional
ship it - Display a motivation squirrel


パグの画像を返してくれる'ああ^~こころがぴょんぴょんするんじゃあ^~'なコマンドがあるらしいので試してみる。

myhubot> myhubot pug me - Receive a pug
myhubot> http://24.media.tumblr.com/tumblr_mc4u6lwZHr1qf4k86o1_500.jpg

http://24.media.tumblr.com/tumblr_mc4u6lwZHr1qf4k86o1_500.jpg
ああ^~こころがぴょんぴょんするんじゃあ^~

hubotの終了はexit

myhubot>exit

hubotに何かさせたいときはscriptディレクトリ配下にXXX.coffeeを作成して処理を書きます。
CoffeeScriptで書きます

script配下にhello.coffeeというファイルを作成し、挨拶したら爽やかに返してくれるスクリプトを書いてみましょう

module.exports = (robot) ->
  robot.respond /HELLO$/i, (msg) ->
      msg.send "fuck you"

挨拶してみる

myhubot> myhubot hello
myhubot> fuck you ^ ^

前衛的な挨拶ですね。

adapterインストール

adapterというのはHubotとチャットツールをつなぐモジュールのことです。
デフォルトで提供されているものもあれば、自分で作ることもできます。

今回はtwitterのadapterを導入

$ npm install hubot-twitter --save && npm install

twitterは起動時に環境変数としてAPIキー等を渡す必要があるので、sh起動時に必要なものは設定するようにしましょう。
この記事を参考にさせていただきました。qiita.com

#!/bin/sh

export HUBOT_TWITTER_KEY="自分のAPI key"
export HUBOT_TWITTER_SECRET="自分のAPI secret"
export HUBOT_TWITTER_TOKEN="自分のAccess token"
export HUBOT_TWITTER_TOKEN_SECRET="自分のAccess token secret"

export HUBOT_NAME="自分のbotの名前"

bin/hubot -a twitter

'bin/hubot -a twitter'の'-a'はadapterを明示的に指定する際のオプションです。
これでローカルでhubotを起動してtwitter上で動作するようになりました。

herokuデプロイ

herokuから参照するprocfileの編集。前述と同じ理由でtwitterのadapterを指定。

bin/hubot -a twitter


ここで一旦gitにコミットしておく

$ git init
$ git add .
$ git commit -m "initial commit"
$ git remote add origin git@XXXXXXXX

いよいよherokuにデプロイ

$ heroku login
$ heroku create
$ git push heroku master

shでsetしたものと同様の設定をherokuにも行ってあげます

$ heroku config:add HUBOT_TWITTER_KEY="自分のAPI key"
$ heroku config:add HUBOT_TWITTER_SECRET="自分のAPI secret"
$ heroku config:add HUBOT_TWITTER_TOKEN="自分のAccess token"
$ heroku config:add HUBOT_TWITTER_TOKEN_SECRET="自分のAccess token secret"

qiita.com

(おまけ)Cronで動かす

hubotは基本的に特定の発言に対して特定の動作を行いますが
Cron使って定時起動 > 自発的に発言できるようにしたいなぁ、と。

以下を参考にしてCronで動くようにしました。qiita.com


まずCronを導入するためにpackage.jsonに以下を追記

"cron": "^1.0.5",
"time": "^0.11.0"

Scriptは参考記事ほぼまんまでこんな感じで。
Cronで毎分10,20,・・・50秒のときにツイートを行うという簡単なもの。

module.exports = (robot) ->
    cronjob = new cronJob(
        cronTime: "00,10,20,30,40,50 * * * * *"
        start:    true
        timeZone: "Asia/Tokyo"
        onTick: ->
            d = new Date
            min = d.getMinutes()
            sec = d.getSeconds()
            message = "#{sec}secなう!"
            robot.send {user:{user:'XXXXX'},screen_name:'XXXXX', room: 'Twitter'}, "#{sec}秒なう!"
    )

これで一通りの導入、動作確認は完了。
これを使って何か作ってみよう。

はてな記法使ってるんだけどshのシンタックスハイライトの効き方がとても。。。微妙です。

twitterのadapterについて

ここで導入したtwitterのadapterはStreaming APIを使用してタイムラインを探索しています。
以前私の記事で取り上げましたが、Streaming APIは全ツイートを取得できるわけではないので稀に漏れが発生することに注意ですsusunshun.hatenablog.com

そうそう最近

うちはコテコテのSIerなので自分で何か作ってみたりプログラミングがはぁはぁ、って人は少ないのですが
そんな物好き達で集まって自分で作ったものを好き勝手に発表する、という勉強会を開催し始めました。

現在の職場環境は周りにそういう方が少ないですし、自分だけで黙々とやってると途中でだれちゃったりするので良い刺激を貰ってます!

このbotで作りたいものはある程度決まってるので、それが終わったらLチカで満足して以来封印してたラズパイを引っ張り出してくるかな。

ハッカソンデビュー

先日、社内のハッカソンイベントに参加してきました!

ハッカソン自体、初参加なのでいろいろ学ぶべきところは多かったです。。。

ハッカソンって何さ?

端的に表現すると、
”ハック”  × ”マラソン” = ”ハッカソン”
です

短期間(今回は2日)でサービスの考案、開発、プレゼンまでを行って、アイデアと技術を競い合うというイベントです

r25.yahoo.co.jp

今回つくったもの

今回は「未来(2017〜2020年)」がテーマです。
うちのグループでは以下のようなサービスを作りました。

  • ICタグで冷蔵庫の在庫管理
  • Webから在庫情報を閲覧可能
  • 賞味期限が近い食材を使ったレシピを推薦

わいが担当したのは

です。

リーダーから読み込んだ商品情報をAPIに送信、APIで在庫更新という感じですね。

ICタグで在庫管理 〜ICタグリーダー

理想は有効範囲が広いRFIDリーダーを使いたかったのですが、開発期間が短いこともあり今回はNFCで妥協することにしました。(最大の妥協)
NFCリーダーは最近のandroidならばどの端末にも組み込まれているので、androidアプリとして実装しています。

あくまでもプロトタイプなので、色々な理想を忘却して最大限実装を簡略化しています

  1. 商品タグのIDを読み込む
  2. http://XXX.com/api/【商品タグID】にリクエスト送信、API側で商品タグIDを抽出、在庫更新

ソースはこんな感じ

public class MainActivity extends Activity {

	//APIのURL
	private static final String sURL = "http://hackson-10-112187.nitrousapp.com/api/v1/stocks";
	
	StringBuilder sb = new StringBuilder();
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		TextView text = (TextView)findViewById(R.id.tag_id);	//EXTRA_ID出力領域
		TextView text_tag = (TextView)findViewById(R.id.tag_tag);	//EXTRA_TAG出力領域
		
		// インテントの取得
		Intent intent = getIntent();		
		String action = intent.getAction();
		if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) { 
			// NFCからID情報取得
			byte[] ids = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
			
			for (int i=0; i<ids.length; i++) {
				sb.append(String.format("%02x", ids[i] & 0xff));
			}
			text.setText(sb.toString());
			
			// NFCからTAG情報取得
			Tag tag = (Tag)intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        	 	String techStr = "";
            		for (String tech : tag.getTechList()) {
                		techStr = techStr + tech + "\n";
            		}
		        if (techStr.equals("")) {
                		techStr = "no techList.";
            		}
            		//TechListを表示
            		text_tag.setText(techStr);
		}
		
		//GETリクエスト
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					HttpClient httpClient = new DefaultHttpClient();
                    			//APIにリクエスト送信
					HttpGet httpGet = new HttpGet("http://XXXX.com/api/" + sb.toString());
					HttpResponse httpResponse = httpClient.execute(httpGet);
					String str = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
				} catch(Exception ex) {
				}
			}
		}).start();
	}
}

ICタグで在庫管理 〜API

APIは以前Grapeで作成したものを持ち込んでちょろっと改修しようと思ってたのですが、デモ環境で動かず5時間の苦戦のうえ泣く泣くあきらめました。。。

結局Railsで作成したWebアプリのコントローラーでリクエストを受けることにし、30分くらいで作成。
APIなのでコントローラーからは独立させたかったのですが。。。

所詮プロトタイプなのでそういうこだわりは捨てて手っ取り早く実装しちゃうのが吉ですね

開発・デモ環境について

gitを準備する時間も惜しいということで今回はnitrous.ioというオンラインのIDEをチームで使用しました。

jp.techcrunch.com


結構優れもので、同じソースを複数人で改修していても他の人の追記部分がリアルタイムで反映されるため手軽に共同開発するには向いていると思います!

ただ今回は失敗でした。。。というのも

  • 備え付けのLAN環境が激遅で他人の改修が自分の編集画面に反映されるのに遅延が多発、結果コーディングしたはずの箇所が消えるということが何回も起きました
  • LAN激遅につき、コーディングしてる最中に何回も画面が読み込み中になり作業が止まる
  • 挙句の果てに実装して保存、サーバを起動しても追記内容がサーバに反映されていない

等々、今回の設備環境には向いてなかったみたいです。

やはりgitを入れとくべきでしたと反省。

完成

朝04:30までプレゼンの準備、フロントの作成等黙々と作業をした結果、こんなのができました。
(デザインのクオリティが予想してたよりも遥かに高い!Webアプリ担当の方おつかれさまでした)

f:id:susunshun:20150719183854p:plain

終えてみての感想

システムの完成度は重要じゃない

サービスのコンセプト、魅力的なプレゼンが極めて重要だと感じました。極端な話、実装はモック程度でよい気がします。今回うちのチームは実装に大半の時間を費やしたのでここをプレゼンのブラッシュアップにあてればよかったですね。

テーマはやはり”社会課題の解決”×”IOT”がウケる

社会課題はもちろん重要なテーマです。
ただテンプレすぎてツマらないということで敢えて避けたのですが、賞を貰っていたチームは高齢化社会インバウンド需要など軒並み最近よく聞くテーマでしたね。。。
そういう大きなテーマの方が気持ちも乗りやすいので次に参加する際には手を出してみようと思います。

色々ありましたが楽しい!

うちのチームは残念ながら受賞ならず。。。でしたがビジネスを一から考えてサービスまで実装しちゃう、という経験はSIerではなかなかありませんので純粋に楽しかったですね。
スマートウォッチやキネクトを使ったグループもいましたが、そういう新しいことに手を出せるのも大きな魅力だと思います。

次こそは(来年?)受賞したい!

twitterでtodo管理アプリを作る その2 ー twitter gem / ruby

前回記事でアプリの構想について書きました。susunshun.hatenablog.com

今回作るもの

  • twitter api を使って特定のハッシュタグが付いてるツイートをタイムラインから随時取得するバッチ
  • 取得したツイートはDBに格納

REST apiとstreaming api

rubytwitterのタイムラインを抽出するアプリを作る上で、REST api(twitter gem)とstreaming api(tweetstream gem)、二つの選択肢がありましたが今回はREST apiを選びました。

REST api

能動的にタイムラインからツイートを取得する必要がある

”必要がある”というよりはこの形式がAPIでは一般的ですが、

  1. Twiterにhttp接続
  2. クライアントからリクエストを送信
  3. 送信されてきたリクエストに対してTwitterからレスポンスを返却(同じリクエストを送れば同じレスポンスが帰ってくる)
  4. 接続クローズ

という順でツイートを取得します。
なので継続的にツイートを取得した場合は繰り返し上記処理を実行しなければいけないわけです。

APIの利用制限は180回/15分(平均:5秒に1回まで)

接続方式や取得する情報の種類にもよりますが、APIの利用には上限があります。
とはいえ通常の使用ならば全然問題ないくらいの制限です。

streaming api

タイムラインをプッシュで受け取ることができる

どういうことかと言うと、

  1. Twiterにhttp接続
  2. クライアントからリクエストを送信
  3. Twitterからレスポンスを返却
  4. Twitterからレスポンスを返却
  5. Twitterからレスポンスを返却
  6. Twitterからレスポンスを返却
  7. Twitterからレスp (ry

つまり接続を切るまでは延々とレスポンスが送られ続けます。
具体的にはとあるキーワードでタイムラインを検索すると、そのキーワードを含むツイートが行われるたびにレスポンスでそのツイートの内容が送信されてきます。

制限事項

streaming apiはRESTfulではないので”180回/15分”のような制限はありませんが、

  • 1つのアカウントからの同時接続数は1つ
  • 接続が切断されることもある(なので再接続する仕組みを持つことが前提となります)
  • パブリックなタイムラインから取得できるツイートは全ツイートの1%(ユーザー指定の場合はちゃんと全ツイート取得できます)

等々、万能ではありません。

今回はパブリックなタイムラインから特定ハッシュタグが付いているツイートを漏れなく抽出したいので消去方でstreaming apiは無しです。REST apiを繰り返し実行して漏れなくツイートを取得する手法をとります。

twitter gem

rubyではREST apiを利用するためのgem、twitter gem がありますのでそれを導入します。

実はこの時点でほとんどrubyを書いたことがない。gemもよくわかってないw

今回インストールしたのは最新バージョン(5.14.0)。

$ gem list twitter

*** LOCAL GEMS ***

twitter (5.14.0)

いざ、実装

実装にあたり、この記事が大変参考になりました。qiita.com

ただこの記事は使用しているtwitter gemのバージョンが4系なので5系では動きません。
そこは最新のリファレンスを参照して手直し。
File: README — Documentation for twitter (5.14.0)

結果、以下の様になりました。

#encoding:UTF-8
require "twitter"
require 'active_record'
require 'time'

client = Twitter::REST::Client.new do |config|
  config.consumer_key       = "XXXXXX"
  config.consumer_secret    = "XXXXXX"
  config.access_token        = "XXXXXX"
  config.access_token_secret = "XXXXXX"
end

ActiveRecord::Base.establish_connection(
  "adapter" => "sqlite3",
  "database" => "./tweet.db"
)

# ActiveRecordのログを標準出力
# ActiveRecord::Base.logger =Logger.new(STDOUT)

class Tweet < ActiveRecord::Base
end

since_id = 0

# 検索キーワード(static)
KEYWORD = "艦これ"

# 無限ループ
loop do
    # 60秒待機
    sleep(5)
    begin
      # KEYWORDをハッシュタグに含むツイートを取得
      # "-rt"で引用リツイートは除外
      # 前回取得した最後のツイートのid以降のツイートから取得
      client.search("##{KEYWORD} -rt",:count =>2, :result_type => "recent", :since_id => since_id).attrs[:statuses].reverse.each do |tweet|
        
        # ユーザ名、Tweet本文、投稿日を1件づつ表示
        p tweet[:user][:name]
        p tweet[:text]
        p Time.parse(tweet[:created_at]).getlocal # 取得するツイートは時差があるので日本の標準時刻に修正
        
        # DBへTweetを登録
        Tweet.create(:user => tweet[:user][:name] , :body => tweet[:text])

        p "===================="

        # 取得した最後のTweet idを保存
        since_id=tweet[:id]
      end

    # エラーが発生した場合は5秒待ってからリトライ
    rescue Twitter::Error::ClientError
      sleep(5)
      retry
  end
end

参考までにテーブルスキーマ

sqlite> .schema tweets
CREATE TABLE tweets(
 id integer primary key,
 user text,
 body text,
 created_at,
 updated_at
);

実行結果

$ ruby getTweetnew.rb
"モノ★モノ.jp 相互フォロー100%"
"#艦隊これくしょん #艦これ 島風\n#コスプレ 6点セット \n[楽天] http://t.co/qkHULFscKu\n\n #RakutenIchiba http://t.co/iq0P5fuZjX"
2015-05-14 01:14:53 +0900
"===================="
"Kazuhiro Wakatsuki"
"ローマを求めてE-6甲に出撃すること2回。どちらもボス前で撤退。攻略中に使わなかった艦娘を多数投入したのが原因と思われる。一度攻略時の編成に戻して、少しずつ入れ替えするしかなかろう。 #艦これ"
2015-05-14 01:15:03 +0900
"===================="
(以下省略)

DBにも無事格納されています。

sqlite> select * from tweets;
1|モノ★モノ.jp 相互フォロー100%|#艦隊これくしょん #艦これ 島風
#コスプレ 6点セット 
[楽天] http://t.co/qkHULFscKu

 #RakutenIchiba http://t.co/iq0P5fuZjX|2015-05-13 16:15:25.727867|2015-05-13 16:15:25.727867
2|Kazuhiro Wakatsuki|ローマを求めてE-6甲に出撃すること2回。どちらもボス前で撤退。攻略中に使わなかった艦娘を多数投入したのが原因と思われる。一度攻略時の編成に戻して、少しずつ入れ替えするしかなかろう。 #艦これ|2015-05-13 16:15:25.734683|2015-05-13 16:15:25.734683

twitter gemは以外とリファレンスが少なく結構手こずりましたが、雛形はこんな感じで!

5系は特に情報が確立していないですし、since_id周りで不具合があったので、twitter gemではなくOAuth認証を使うという選択肢もあるかもしれませんね。(もちろん使いこなせればtwitter gemの方が便利ですが!)

qiita.com

twitterでtodo管理アプリを作る その1

仕事ではjavaを使っていますが、いまのご時世rubypythonがWebアプリではメジャーとなっているので、rubyの勉強がてらtwitterでtodo管理をするアプリを作ってみようと思います。(飽き性なので完遂できる可能性小)

どんなアプリ?

最低限実装したい機能
  • twitterで特定ハッシュタグ(以下固定タグ)をつけてツイートするとtodoリストに追加される
  • 固定タグ以外にもオプションとして、todoのカテゴリ、期限を別ハッシュタグで付与できる
  • todoの完了もtweetから操作できる
  • todoは別のWebページから閲覧・編集できる(twitterアカウントごとに管理、ログイン機能なし、つまり他のユーザのtodoも閲覧できてしまう)
  • 似たようなアプリがあるかは調べてない
できれば実装したい機能
  • todoの期限が近づいたらtweetのリプライで通知してくれる
  • 定期的に未完了タスク一覧を通知
  • google todoやevernoteとの連携(todoだけじゃなくて”あとで読む”機能とかね)
  • todoは別のWebページから閲覧・編集できる(ログイン機能あり)

どうやって作んの?

くそざっくりですが、twitter apiを使って固定タグが付与されているtweetを抽出・DBに格納する常駐バッチ作ります。抽出したtweetのうち他にハッシュタグ(カテゴリ、期限など)の指定があればそれに応じた処理を行う。

環境

  • バッチはruby+active record, twitter gem*1で作成
  • 画面系はsinatrarailsかなぁ(メインはtwitterからの利用なのでそんなに凝るつもりなはない。BootStrap使うかも)
  • まずはローカルで作るのでDBはとりあえずsqlite

私のスペック

つまりrubyとかその周辺を勉強してみたいけど、参考書読み込むよりも作りながら覚えようというスタンスです。


てな感じですかね。
バッチの雛形は実はもう作成してあるんで近々別記事で書きます。

GW終了、死にたい。

*1:プッシュでtweetを取得してくれるtweetstreamではなく、twitter gemを用いて定間隔でタイムラインからtweetを取得する方式にしました。理由は後日。

sinatraの開発環境構築からHelloWorldまで

sinatraの環境構築めもです。

sinatraって?

RubyのWebアプリケーションフレームワークです。
RubyフレームワークというとRailsが覇権ですが、sinatraRailsに比べて超小規模(画面遷移が数画面)のアプリ開発に向いていると思います。

なんかサクッと作ってみたいだけなのにRailsは実装に際してルール多すぎるんだよ!という方にはオススメ。

超小規模〜小規模 → sinatra
中規模 → Rails

という棲み分けでしょうか。

公式サイト

f:id:susunshun:20150422235950j:plain
Sinatra: README (Japanese)

導入するもの

ruby, gemあたりは以前Railsを勉強していたときに導入したのですが最新バージョンにアップデートします。

Ruby

バージョン確認。最新版は2.2.2

$ ruby -v
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin13.0]

古いのでアップデートします。

$ rbenv install 2.2.2

※10分強かかります

rbenvはRubyのバージョンを管理してくれるツールです。
入れてない方はgoogle先生に聞いてください!

インストールが完了したら、2.2.2を標準で使用するバージョンに指定

$ rbenv global 2.2.2

これでRubyは最新になりました!

$ ruby -v
2.2.2

gem

まずはバージョン確認

$ gem -v
2.2.2

入れてない人はGoogle先せ(ry

古いのでアップデートします

$ gem update --system

gemも最新になりました

$ gem -v
2.4.6

ほか

以下コマンドでインストール

$ gem install sinatra
$ gem install sinatra-contrib
$ gem install activerecord
$ gem install sqlite3

sinatraはインストールが途中で止まってん?ってなりますがドキュメントをダウンロードしているだけらしいのでしばらく待てば完了します。

sinatraを使ってみよう

main.rbの作成

任意のディレクトリで”main.rb”というファイルを作成し、以下コードを書きます。
公式サイトのサンプルコードを少しだけ書き換えました。

require 'sinatra'
require 'sinatra/reloader'
 
get '/' do
    "<h1>Hello Sinatra<h1>"
end
 
get '/hamachi' do
    "<h1>sub page<h1>"
end

getの後の'/'や'/hamachi'はリクエストのパスに対応しています

'/' → http://localhost:4567/
'/hamachi' → http://localhost:4567/hamachi
sinatraのデフォルトのポートは「4567」です

各セグメントのdo〜endに処理内容を記述します。
上記のサンプルコードでは単純にHTMLを出力するだけです。

サーバの起動

今回はrubyにデフォルトで付属しているWebサーバ、WEBrickを使用します。
rubyを導入してれば一緒にインストールされているはずなので以下コマンドでサーバを起動。

$ ruby main.rb

最後にページの確認
http://localhost:4567/
f:id:susunshun:20150422234906p:plain

http://localhost:4567/hamachi
f:id:susunshun:20150422234916p:plain

驚きの白さ。。。っ!!
以上で環境構築は完了!

補足)reloaderについて
sinatraはそのままだとrbファイルを編集した際、その内容をWebサーバに反映させるためには都度サーバを再起動させないといけません。
それはとても面倒。。。
ということでsinatra-contirbのreloaderを使用すれば再起動することなく自動でサーバに編集内容が反映されます。上記サンプルコードの「require 'sinatra/reloader'」はそのための記述です。