2009-08-16
GAE/Python で Twitter bot を作る(後編)
stackstockbooks, bit.ly, twitter, api, gae, python
準備編 の続きは明日書くとか言っときながら、間が空いてしまいましたが後編を。
残るは肝心のハンドラ部分を実装していきます。
views.py の作成
main.py で指定したように、
- /:TOP画面。ハンドラは MainHandler
- /ssbbot/mumble/update/:cron がここを叩いて更新処理を走らせる。ハンドラは BotHandler
という感じで views.py を実装して行きましょう。
#!/usr/bin/env python # -*- coding:utf-8 -*- import logging from google.appengine.ext import webapp import twitter import ssb import bitly from models import MumbleModel def my_twitter_api_init(self, username=None, password=None, input_encoding=None, request_headers=None): """Monkey patche for twitter.Api.__init__ method. Just change _cache do not use FS cache. """ import urllib2 from twitter import Api self._cache = None self._urllib = urllib2 self._cache_timeout = Api.DEFAULT_CACHE_TIMEOUT self._InitializeRequestHeaders(request_headers) self._InitializeUserAgent() self._InitializeDefaultParameters() self._input_encoding = input_encoding self.SetCredentials(username, password) twitter.Api.__init__ = my_twitter_api_init class MainHandler(webapp.RequestHandler): def get(self): """ """ self.response.out.write("Stack Stock Books つぶやき Bot") class BotHandler(webapp.RequestHandler): """Bot 更新用ハンドラ""" def get(self): """ """ # 最新の SSB つぶやき取得 ssbapi = ssb.Api() mumble = ssbapi.get_mumbles(include_books=False)[0] logging.debug("mumble: %s", mumble) key_name = MumbleModel.key_format % dict(mumble_id=mumble.mumble_id) mm = MumbleModel.get_by_key_name(key_name) if mm is not None and mm.time >= mumble.time: # 更新情報なし logging.info("No update: mumble_id: %s", mumble.mumble_id) self.response.out.write("No update") return # 書籍情報取得 book = ssbapi.get_book(mumble.book_id) mumble.book = book # 投稿文言生成 twitterapi = twitter.Api(username="TWITTER_USER", password="TWITTER_PWD") user = ssbapi.get_user(mumble.user_id) status = self._mumble_string(user, mumble) if len(status) > 140: status = status[:139] + u'\u2026' # Twitter 更新 result = twitterapi.PostUpdate(status) logging.info("status: %s\nresult: %s", status, result) # 更新情報を登録 mm = MumbleModel(key_name=key_name, mumble_id=mumble.mumble_id, time=mumble.time) mm.put() self.response.out.write("OK") def _mumble_string(self, user, mumble): url = mumble.uri api = bitly.Api() api.set_account("BITLY_LOGIN", "BITLY_APIKEY") try: url = api.shorten(url) except bitly.BitlyResponseError: pass return u"%(url)s %(nick)s『%(title)s』%(body)s" \ % dict(nick=user.nick, title=mumble.book.title, body=mumble.body, url=url)
BotHandler では、
- Stack Stock Books から最新のつぶやき取得
- Datastore に保存した前回のつぶやき情報と比較。更新が無ければ終了。
- 新しい情報ならば、Twitter への投稿文言生成。URL は bit.ly API を利用して短縮。
- 文言が140文字を越えていたら、139文字+'…' として切り詰め処理。
- Twitter へ投稿
- Datastore に最新つぶやき情報を登録
という処理をしています。
完成!
さて、これで簡単な Twitter bot ができました。ファイル構成は以下のようになっていると思います。
./ssbbot/ |-- app.yaml |-- bitly.py |-- cron.yaml |-- main.py |-- models.py |-- simplejson |-- ssb.py |-- twitter.py `-- views.py
2009-08-10
GAE/Python で Twitter bot を作る(準備編)
stackstockbooks, bit.ly, twitter, api, gae, python
先のエントリで bit.ly API モジュールやら Stack Stock Books API モジュールを作ったので、Google App Engine 上で動作する Twitter bot を作ってみます。
先のエントリとやらはこの辺です。
どんな処理にしましょうか?
ざっくりとこんな感じで。
- Stack Stock Books から最新のつぶやき取得
- Datastore に保存した前回のつぶやき情報と比較
- 新しい情報ならば、Twitter への投稿文言生成
- Twitter へ投稿
- Datastore に最新つぶやき情報を登録
- これらの処理を一定間隔で回す
ということで、URL 的にはこんな感じに。
- /:紹介文でも表示しましょう
- /ssbbot/mumble/update/:cron がここを叩いて上記処理を走らせる
必要なモジュールのソースをダウンロード
以下のモジュールのソースを取得します。
そして、先のエントリで自作したモジュール。
これらのモジュールのソースコードを ssbbot ディレクトリ以下に配置します。
./ssbbot/ |-- bitly.py |-- simplejson |-- ssb.py `-- twitter.py
ちなみにキモの python-twitter は 0.6 を使用していますが、Api クラスにおいてファイルキャッシュを使っているために、素のままでは GAE で使えません。今回は以下のような Monkey patch を twitter.Api を利用する前に書いて、その場しのぎを…
def my_twitter_api_init(self, username=None, password=None, input_encoding=None, request_headers=None): """Monkey patche for twitter.Api.__init__ method. Just change _cache do not use FS cache. """ import urllib2 from twitter import Api self._cache = None self._urllib = urllib2 self._cache_timeout = Api.DEFAULT_CACHE_TIMEOUT self._InitializeRequestHeaders(request_headers) self._InitializeUserAgent() self._InitializeDefaultParameters() self._input_encoding = input_encoding self.SetCredentials(username, password) twitter.Api.__init__ = my_twitter_api_init
アプリケーション設定ファイルの作成
ssbbot ディレクトリに、アプリケーション設定ファイル app.yaml を以下のような内容で追加します。
application: YOUR_APP_ID version: 1 runtime: python api_version: 1 handlers: - url: /ssbbot/.* script: main.py login: admin - url: /.* script: main.py
今回は /ssbbot/以下の URL は cron (と管理者)だけが叩けるようにしたいので、login: admin で、管理者権限を持つユーザーのみがアクセスできるようにしています。
スケジュールタスク設定ファイルの作成
更新処理を cron で処理を走らせたいので、ssbbot ディレクトリにスケジュールタスク設定ファイル cron.yaml を以下のように追加します。
cron: - description: update mumble job url: /ssbbot/mumble/update/ schedule: every 30 mins
ここでの間隔は30分にしていますが、あまり間隔が空いてしまうと更新データの取りこぼしが出てしまいますし(今回のアプリでは最新の一件しか投稿しませんw)、かと言って短か過ぎると、利用 API や GAE の制限に引っかかったり迷惑をかけてしまうので程々にします。
models.py の作成
Twitter に投稿されたつぶやき情報を保持するため、MumbleModel という Datastore モデルを作成します。
ssb モジュールの Mumble クラスは多くのプロパティを持ちますが、今回は既に投稿済みかどうかを比較するためだけに使用するので、保持するプロパティは mumble_id と time、そしてデータストアに登録した日時だけにしています。
#!/usr/bin/env python # -*- coding:utf-8 -*- from google.appengine.ext import db class MumbleModel(db.Model): """ >>> mumble = Mumble() >>> mumble.mumble_id = 123 >>> mumble.time = datetime(2009, 8, 3, 22, 34, 36) >>> mumble.put() >>> db.put(mumble) """ key_format = "mumblemodel/%(mumble_id)s" mumble_id = db.IntegerProperty(required=True) time = db.DateTimeProperty(required=True) created = db.DateTimeProperty(auto_now_add=True)
main.py の作成
ハンドラに行く前に、メイン処理を書いちゃいます。
#!/usr/bin/env python # -*- coding:utf-8 -*- from google.appengine.ext import webapp from google.appengine.ext.webapp.util import run_wsgi_app from views import * def main(): app = webapp.WSGIApplication([ (r'/', MainHandler), (r'/ssbbot/mumble/update/', BotHandler), ], debug=True) run_wsgi_app(app) if __name__ == '__main__': main()
よし、これで残りは views.py に肝心のハンドラを書くだけです。
明日あたりに続くっ!
