映画『サマーウォーズ』の小磯健二のように、誕生日から曜日を計算して教えてくれるtwitterのbotを作った

koiso kenji (@koiso_bot) | Twitter

  • yyyy/mm/dd の形式でreplyすると、その日の曜日を教えてくれる
  • それ以外の形式はすべて無視
  • 1分間隔で10Postまで返答する それ以上は古いものから切り捨て




Python,Twitter,GAE,webアプリ すべてが初心者マークですが、初心者なりにいろいろ考えて作ったので時系列にまとめておく。




なぜ小磯?

昨日の勉強会の時点で、botに定期的に発言させるというところまでは進んでいたので、次はreplyに対して何か返答してくれるbotを作りたいと思った。
そこで思いだしたのが映画『サマーウォーズ』で、主人公の健二が誕生日からその日の曜日を計算で言い当てるシーン。
決まった形式を受け取って、リアクションを返すという意味で、今回の自分の趣旨にぴったりだった。




計算部分のロジックを考える

とりあえず既存の知識だけでなんとかなりそうな計算のロジックを考えた。
式は以下をそのままパク・・・参考にした。


   生年月日で曜日を求める方法 - 映画サマーウォーズでやっていた生年月日で... - Yahoo!知恵袋

# 曜日を準備
week = { 1:'日', 2:'月', 3:'火', 4:'水', 5:'木', 6:'金', 0:'土' }

 〜 略 〜

# 日付を取り出し分割
date = tweet['text'].encode('utf-8').split()[1]
split_date = date.split('/')

# 曜日の計算
h = int(split_date[0][:-2])
y = int(split_date[0][-2:])
m = int(split_date[1])
d = int(split_date[2])
w = y + y / 4 + h / 4 - 2 * h + 13 * ( m + 1 ) / 5 + d
key = w%7

koiso_tweet = '@%s %s曜日ですっ、%sは%s曜日です 当たってます?' 
    % ( re_name, week[key], date, week[key] )


いまさら思ったけど、日付から曜日を簡単に求められるパッケージ(?)とかあったりして。




自分へのreplyを取得する方法を考える

はっきり言って何もわからない、とりあえずtwython.pyの中身を眺めてみる。
もしやreplyと付くそれっぽいメソッドがあるのでは?!と思い検索してみる。
みつからない。


githubにサンプルコードがあると教えてもらった事を思い出す。


   twython/examples at 0d37e5be404f5aaf36ae88e999ef06ffc5e5ccf8 · ryanmcgrath/twython · GitHub


全部試してみてgetUserMentions()がreplyを拾うのに使えそうだと判断した。




同じ発言に対して2回以上replyしないようにする

githubのサンプルコードのようにcount指定だけだと同じ発言に対して2回以上返答してしまう可能性がありそうなので、その制御の処理を考え始める。
これが一番わからなかった。
一度つぶやきを返したものをどこかに記憶しておけばいいのか、でもそうなるとGAEのデータベースとか使うのかーとか、あーだこーだ思ったけれど、「自分が最後につぶやいたとき」を基準にそれ以降の自分へのreplyを対象にすればいいという事で落ち着いた。
つぶやきのデータ内に時系列を判断できるデータがあればそれができる。


getUserMentions()で受け取ったデータの中身を見ながら、使えそうな要素をさがしてみる。
日時っぽいものはあるけどそれをそのまま使えそうにないし、Twitter側でなんか用意してないのかと思って、そこで始めてTwitter側の仕様、つまりAPIがわかってないとダメということに気がつく。


   [観] Twitter API 仕様書 (勝手に日本語訳シリーズ)


引数でsince_idを渡してあげると、それ以降のタイムラインを取得してくれる。
そうかー、とここでidを使えば時系列を判断できるのだと理解した。

# 自分の最後のtweetのステータスidを取得
mytweet = api.getUserTimeline(count='1')
mytweet_id = mytweet[0]['id']

# 最後のtweet以降の自分へのreplyを取得
mymentions = api.getUserMentions(since_id=mytweet_id)


あとはそんなにひっかかるところもなく完成までこぎ着けた。
あ、一度cronが動かなくなってあせったけど。




まとめ

いろいろ紆余曲折したけれど、なんとか形になってよかったです。
TwitterAPIについてはもうちょっと確認が必要。
あとは、受け付けるつぶやきの形式に幅をもたせたり、何もないときでもたまーにつぶやいたりできるといいなあと思います。


GAEの制御についても調べたいし、まだまだやりたいこといっぱいあるなー。

「GAEでTwitterのbotを作る #1(通称『ぼっつく』)」に参加してきた

はじめて勉強会というものに参加してきました。


GAEでTwitterのbotを作る #1 : ATND


印象に残ったこと箇条書

  • すばらしい会場とその設備(しかも使用料タダ!
  • しかし手違いにより冷房はOFFという環境に配慮したエコな設定
  • Mac多っ!
  • タイムラインが一時、おっぱいがいっぱい状態に


まず会場を用意してくださった方にに感謝!
内容は、基調講演でGAE、Twitterbotとは何ぞや?というところから始まって、それからそれぞれのチームに分かれて黙々とコーディングを行いました。
僕はPythonを始めたばかりだったので、用意していただいていたハンズオンの資料を見ながら必死で課題をこなし、とりあえず1分ごとにbotが発言するところまではクリア。
途中、エンコードの関係でエラーが出たりcronの設定が上手く行かなかったりしてましたが、周りの方々に助けのおかげで解決する事ができました。
助けてくださった方々、本当にありがとうございます。


その後は懇親会に参加。
はっきりいって皆さんの話題について行けず、ぼけーっとしてましたが、それでもwebの世界の片鱗に触れられたというだけで、自分にとっては良い刺激になりました。


なんか自分のことばっかだなぁ、正直あんまりまわり見る余裕がなかったです。
また第2回、第3回と勉強会は続くので、それまでにもっと色々試してみたいと思います。




<追記>


http://d.hatena.ne.jp/kidd-number5/20090816/1250389946


x___x


http://d.hatena.ne.jp/comaz777/20090817/1250491527

イテレータとはなんですか ― Python勉強中 10

最近サボり気味だったけれどちょっとずつ再開。
イテレータについてまとめてみる。


まず、みんpyにはこう書いてある。

複数の要素を持ったデータの要素を順番に取り出すとき、より汎用的な手法を提供するのがイテレータです。

ようするに、色んなデータ型で使える「データを順番に取り出す方法」を提供してくれるらしい。


色んなデータ型で使えるのでルールはいたってシンプル。

  • 次の要素を取り出す
  • 要素が終わったかどうかを判別する


以上2つだけ。

>>> for t in (1,2,3):
...     print(t)
... 
1
2
3
>>> for l in [1,2,3]:
...     print(l)
... 
1
2
3
>>> for d in {1:'a',2:'b',3:'c'}:
...     print(d)
... 
1
2
3

これまで見てきたfor文も、タプルやリストや辞書のデータ型を裏でイテレータオブジェクトへ変換しているので、自動的に順番に取り出す事ができているということみたいです。


Pythonでは、イテレータオブジェクトへ変換する関数として iter() が用意されています。また、次の要素を取り出す処理を next() メソッドでおこない、要素がなくなると StopIteration という例外が発生します。

>>> i = iter([1,2,3])
>>> i.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list_iterator' object has no attribute 'next'

あ、あれっ!?
ん…あー、Python3 では next() ではなく __next__() のようです。

>>> s = iter('string')
>>> s.__next__()
's'
>>> s.__next__()
't'
>>> s.__next__()
'r'
>>> s.__next__()
'i'
>>> s.__next__()
'n'
>>> s.__next__()
'g'
>>> s.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

これでよし。

macでGoogleAppEngineでhelloworld

勉強会の宿題として。





以下のURLから Google App Engine 用のクライアントソフト『Google App Engine Launcher』をダウンロードする。


http://code.google.com/intl/ja/appengine/downloads.html:http://code.google.com/intl/ja/appengine/downloads.html




Google App Engine Launcherをインストールし起動する。




File > New Application もしくは command + N で新しいアプリケーションを作成、Application Name を入力し Create を実行する。




すると Application Directory で指定したフォルダ内にアプリケーションのフォルダが作成される。




アプリケーションのフォルダ内に作成されるファイル。




フォルダ内に新しく helloworld.py を作成し、中身は以下のようにする。

print 'Content-Type: text/plain'
print ''
print 'Hello, world!'


app.yaml を開き、main.py の部分を helloworld.py に書き換えて上書き保存する。

application: ka-z-z-z-ya
version: 1
runtime: python
api_version: 1

handlers:
- url: .*
  script: helloworld.py

ここまでで作成は完了。




ローカルでテストする。
Control > Run もしくは command + R でアプリケーションの実行。
Control > Browse もしくは command + B で動作を確認。




ローカルでの動作が確認できたら、いよいよwebへアップロード。
あらかじめグーグルのアカウントを用意し、アプリケーションを登録しておく。

Application Identifier は Launcher で作成したときのアプリケーション名と一致させないとデプロイ時にエラーとなる。




Launcher でデプロイを実行。Control > Deploy あるいは comannd + D。
urlを開くと helloworld が表示される。




なんか駆け足になってしまった。

映画『サマーウォーズ』を観た感想

プロモーションでは「家族」というキーワードが前面にでているけれど、もっと大きなテーマ「人とのつながり」というものをすごく感じた。
特にサカエばあちゃんが、遠くにいる親戚や知り合い、今では疎遠になっているのであろう古き友人たちに「あなたならできる!」と電話をかけ続けるシーン。
ここに、何よりも「人とのつながり」を大切にする気持ちと、それを形にする「行動力」があらわれていると思う。


別に僕らは世界を救う必要はない。
けれども「つながり」を大切にする気持ち、それとほんのちょっとの「行動力」で人生をもうちょっとだけ楽しめるんじゃない?て事を、サカエばあちゃんは教えてくれている気がする。

Python勉強中 9

リスト内包表記を勉強したので、前回作ったコードを書き直してみた。

rfile = open('kanshi.log', 'r')
wfile = open('err.log',  'w')

wfile.writelines(
    [line for line in rfile.readlines() if 'ERR' in line])

rfile.close()
wfile.close()

なにこれこわい。


一行で書けるのは便利かもしれないけど読みづらいなぁ。
慣れの問題かな?

Python勉強中 8

ファイルの入出力のところまで読んだので試してみる。
ログファイルからエラーのログのみを抜き出したいので、kanshi.logから'ERR'の文字を含む行を抽出して、err.logに書き出す。

rfile = open('kanshi.log', 'r')
wfile = open('err.log',  'w')

for line in rfile.readlines():
    if 'ERR' in line:
        wfile.write(line)

rfile.close()
wfile.close()

まぁこのくらいの事はエディタ使えばさらっと出来てしまうのだけど。
そんな事はいーんだ。