クリスマスまでに彼女を作る方法?
12/23:18:00ぐらい 独自RTをされると無限ループに陥る箇所があったので修正しました。
とても素晴らしいエントリーを見つけてしまったのでやる気を出してみた。既婚者だけど。
GoogleAppEngine + JRubyでクリスマスまでに彼女をつくる方法
ただしRubyとかよくわからんのでPythonで似たようなものを実装。
とりあえず次の手順で仮想彼女を作成する事が?できるはず?
1:このファイルをダウンロードして解凍した後に 2:Python-Twitterのファイルをlib以下にpython-twitterという名前で保存 3:setting以下のtwitter.yaml、messages.yaml、call_rsponses.yaml、basic.yamlを編集 4:Google App Engineに各ファイルを登録
messages.yamlが自動発言用のデータ、call_responses.yamlが自動応答用のデータファイルになるので、頑張って充実させればさせるほど素敵なボットになると思います。。。多分。
一応データ設定例のひとつとして、ミュージカル「RENT」の台詞に関する自動発言と自動応答を行うBOTを実装しています。→ RENT BOT
とりあえず動くようにしました!レベルなので、今後時間を見つけて直していきます。
以下、やっつけの説明。時間があるときにでもきちんと書き直します。多分。
1:とりあえずGoogle App EngineのアカウントやSDKを取得
アカウント:https://appengine.google.com/
SDK:http://code.google.com/intl/ja/appengine/
Google App Engineに関する詳しい説明は、詳しいサイトがたくさんあるので適当にググってください。
2:アプリケーションの設定ファイルを作成
Google App Engineのアプリケーション設定ファイルのapp.yaml、cron.yamlを次のように作成します。
なお、Google App EngineのCronはアプリケーションのURLを叩く形で実行されます。今回は「/response」がリプライへの反応、「/post」が発言用のURLです。
・app.yaml
application: bot version: 1 runtime: python api_version: 1 handlers: - url: /response script: batch.py login: admin - url: /post script: batch.py login: admin
cron.yaml
cron: - description: replay job url: /replay schedule: every 5 minutes - description: post job url: /post schedule: every 15 minutes
cron.yamlの設定で発言の周期を設定することができます。
3:python-twitterを取得してラッパーを作成
PythonでTwitter APIを扱うためのライブラリPython-Twitterを下記URLから取得します。
http://code.google.com/p/python-twitter/
またpython-twitterはそのままではGoogle App Engine上で動作しないので簡単なラッパーを作成します。
#!/usr/bin/env python # -*- coding: utf-8 -*- ### 外部モジュールの読み込み ######################## import sys # python-twitterをimport import twitter # twitter側の処理 class PythonTwitter: def __init__(self, account, password): self.message_list = [] ### botアカウントにapi接続 ########################## def my_twitter_api_init(self, username=None, password=None,input_encoding=None, request_headers=None): """overriding twitter.Api.__init__ method not to 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 self.api = twitter.Api(account, password) ### Replay発言を取得して格納 ############################## def getReplay(self, dt, id): replay_list = [] replies = self.api.GetReplies(dt, id) page = 1 while 19 <= len(replies): add_replies = self.api.GetReplies(dt, id, page) replies = replies + add_replies page += 1 self.message_list.append('------ getReplay'); for replay in replies: replay_list.append({'text':replay.text, 'id':replay.id, 's_name':replay.user.screen_name, 'dt':replay.created_at, 'time':replay.created_at_in_seconds}) self.message_list.append('message: ' + replay.text); return replay_list ### POST ###################################### def post(self, message): return self.api.PostUpdates(message)
4:アプリケーション処理を作成
Google App Engineで動作する処理ファイルを作成します。基本的にはtwitter-pythonをラッパー経由で呼び出してPOST、またはReplayを取得してそれに応じた発言をPOSTするという流れです。
なお「とりあえず動けば良いや」で書いているので、計算量とかはあんまり考えていないです。なのでもっと効率的なロジックはたくさんあると思います。。。というよりあります。ごめんなさいちゃんと勉強します。
それと、リプライの取得はGoogle App Engineのデータストアに最終更新時間を持たせる事で若干効率よくしています。API経由での取得制限対策のためで、あくまで若干ですが。
#!/usr/bin/env python # -*- coding: utf-8 -*- # Google App Engineライブラリの読み込み import wsgiref.handlers from google.appengine.ext import webapp from google.appengine.ext.webapp import template # アプリケーションパスを読み込み import sys import os sys.path.append('lib') sys.path.append('lib/python-twitter') sys.path.append('class') sys.path.append('model') ### 基本モジュールを読み込み import random import yaml import re ### twitter処理を読み込み import pythonTwitter ### modelを読み込み #import report ### classを読み込み ### 設定値 setting_dir = 'setting/' ### つぶやき処理 ######################################################## class post(webapp.RequestHandler): def __init__(self): self.setting_dir = setting_dir def get(self): file_data = open(self.setting_dir + 'twitter.yaml').read() data = yaml.load(file_data) account = data['account'] password = data['password'] # Python Twitter経由でTwitterに接続 obj = pythonTwitter.PythonTwitter(account, password) file_data = open(self.setting_dir + 'messages.yaml').read() data = yaml.load(file_data) messages = data['messages'] message_num = len(messages) key = random.randint(0, message_num - 1) obj.post(messages[key]) ### リプライ処理 ######################################################## class response(webapp.RequestHandler): def __init__(self): self.setting_dir = setting_dir def get(self): file_data = open(self.setting_dir + 'twitter.yaml').read() data = yaml.load(file_data) account = data['account'] password = data['password'] file_data = open(self.setting_dir + 'basic.yaml').read() data = yaml.load(file_data) default_response = data['default_response'] # call & response定義 file_data = open(self.setting_dir + 'call_responses.yaml').read() data = yaml.load(file_data) call_responses = data['call_responses'] # replay Modelの取得 import replay rModel = replay.ReplayStatusModel() last = rModel.getLastStatus() # Python Twitter経由でTwitterに接続 obj = pythonTwitter.PythonTwitter(account, password) replies = obj.getReplay(last['dt'], last['id']) if last['id'] == None: init_flag = True else: init_flag = False last_id = last['id'] last_time = last['time'] last_dt = last['dt'] for replay_item in replies: if init_flag == False: replay_message = replay_item['text'] to_pattern = re.compile('^.?@' + account + ' ') replay_message = to_pattern.sub('', replay_message) replay_message = replay_message.strip() response_result = False for list in call_responses: if list['call'] == replay_message: response_message = list['response'] response_result = True if response_result == False: response_message = default_response obj.post('@' + replay_item['s_name'] + ' ' + response_message) if last_time < replay_item['time']: last_id = replay_item['id'] last_time = replay_item['time'] last_dt = replay_item['dt'] rModel.setLastStatus({'id':last_id, 'time':last_time, 'dt':last_dt}) ### メイン処理 ######################################################## def main(): application = webapp.WSGIApplication([ ('/post', post), ('/response', response) ], debug=True) wsgiref.handlers.CGIHandler().run(application)
5:リプライ取得時間格納用のモデルを作成
リプライの最終取得時間の格納用のモデルを次のように作成します。ここではGoogle App Engineのデータストア用のモデル定義とデータ処理用のクラスをまとめて記述してあります。
#!/usr/bin/env python # -*- coding: utf-8 -*- from google.appengine.ext import db ### Replay状態関連のデータモデルクラス #################################### class ReplayStatus(db.Model): update_id = db.IntegerProperty() update_dt = db.StringProperty() update_time = db.IntegerProperty() update_status = db.StringProperty() create_dt = db.DateTimeProperty(auto_now_add=True) ### ReplayStatusモデルに関する処理クラス #################################### class ReplayStatusModel: def getLastStatus(self): if 0 < ReplayStatus.all().count(): data_set = ReplayStatus.all().order('-create_dt').fetch(1) return {'id':data_set[0].update_id, 'time':data_set[0].update_time, 'dt':data_set[0].update_dt} else: return {'id':None, 'time':None, 'dt':None} def setLastStatus(self, update_info): if 0 < ReplayStatus.all().count(): update_obj = ReplayStatus.all().order('-create_dt').fetch(1) dt_obj = update_obj[0] dt_obj.update_id = update_info['id'] dt_obj.update_time = update_info['time'] dt_obj.update_dt = update_info['dt'] else: dt_obj = ReplayStatus(update_id=update_info['id'], update_time=update_info['time'], update_dt=update_info['dt']) dt_obj.put()
6:ボット用のデータ、設定を作成
Twitterボットとして動作させるための設定を作成します。
ボット用のtwitterアカウント情報を記述します
account: <アカウント> password: <パスワード>
・basic.yaml
ボット用の基本情報を記述します
bot_name: <ボットの名前> bot_description: <ボットの説明> default_response:<デフォルトの応答文>
・messages.yaml
自動POST用の文章を記述します。文章は複数記述できます。
messages: - "メッセージその1" - "メッセージその2"
・call_responses.yaml
自動応答用の想定問題集を記述します。callで定義される内容がリプライされた場合にresponseで定義された内容が返されます。応答セットは複数記述できます。
call_responses: - call: "問いかけ 1" response: "応答 1" - call: "問いかけ 2" response: "応答 2"
7:各ファイルをパッキングしてデプロイ
app_cfg.pyを利用してアプリケーションをでプロイします。