GAE/Pyを使ってTwitter本アカのふぁぼを自動ではてブ+サブアカに公式RTする。

Google App Engine を使ってTwitter本アカのふぁぼったツイートに含まれるリンクを自動ではてブ+サブアカに公式RTする。
はてブからTwitterに流す設定をしている場合、AtomAPIからの投稿をTwitterに流さないようにしておく。ブコメに@を使っているので、ふぁぼったツイートの発言者さんに@飛ばすことを防ぐため。

ついでにGoogle Readerのスター付きアイテムを自動ではてブTwitterサブ垢に流す。

  • 必要なもの

oauth.py
GAEのpython用oauthライブラリ 
https://github.com/mikeknapp/AppEngine-OAuth-Library
WSSE.py
WSSE認証を利用したAtom APIとか - YAMAGUCHI::weblog
http://d.hatena.ne.jp/ymotongpoo/20081201/1228115936

[詰まったところ]
Google Readerスター付きアイテムのAtomフィードをElementTreeだけでパースするところ。
・Task queueの使い方。

app.yaml

application: XXXXXXXXXXX #アプリケーション名
version: XX
runtime: python
api_version: 1

handlers:
- url: /.*
  script: main.py
  login: admin

cron.yaml

cron:
- description: twitter favs
  url: /regist_favs
  schedule: every 10 minutes
  timezone: Asia/Tokyo

- description: Google Reader Stars
  url: /regist_GStars
  schedule: every 10 minutes
  timezone: Asia/Tokyo

queue.yaml

queue:
- name: default
  rate: 1/s
- name: bookmarkGStar
  rate: 1/m
  retry_parameters:
    task_retry_limit: 7
    task_age_limit: 2d
- name: bookmarkTwFav
  rate: 1/m
  retry_parameters:
    task_retry_limit: 7
    task_age_limit: 2d
- name: retweet
  rate: 1/m
  retry_parameters:
    task_retry_limit: 7
    task_age_limit: 2d
- name: tweetGStar
  rate: 1/m
  retry_parameters:
    task_retry_limit: 7
    task_age_limit: 2d

main.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = "yadokari23"

import oauth
import os

import urllib, urllib2

from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext import db

from google.appengine.api import urlfetch

from django.utils import simplejson

from xml.etree.ElementTree import *

import datetime

from WSSE import *

from google.appengine.api.labs import taskqueue

#Twitter アプリの情報
CONSUMER_KEY = "XXXXXXXXXX" #Consumer key
CONSUMER_SECRET = "XXXXXXXXX" #Consumer secret
#投稿先(サブ垢)のもの
ACCESS_TOKEN = "XXXXXXXXXX" #access token
ACCESS_TOKEN_SECRET = "XXXXXXXXXX" #access token secret


#ふぁぼを監視するアカウントのscreen_name(今回は本アカ)
T_ACCOUNT = 'XXXXXXXXXXX'

#はてなアカウント情報
HATENA_USERNAME='XXXXXXX'
HATENA_PWD='XXXXXXX'

#Google Readerのスター付きアイテム公開ページのURL
GSTARURL='XXXXXXXXXXXXXXXXXXXXX'

MaxPage = 1 #MaxPage*20件

class favorites(db.Model):
    status_id = db.StringProperty(required=True)
    text = db.TextProperty()
    created = db.DateTimeProperty()
    url = db.TextProperty()
    user_id = db.StringProperty()
    user_screen_name = db.StringProperty()
    task = db.BooleanProperty(default=False)
    
class GStars(db.Model):
    id = db.StringProperty(required=True)
    link = db.TextProperty()
    title = db.TextProperty()   
    source_title = db.TextProperty()
    author = db.TextProperty() 
    task = db.BooleanProperty(default=False)
    
def get_longURL(shortURL):
    #短縮URLの展開
    try:
        longURL = urllib2.urlopen(shortURL).geturl()
    except:
        longURL = ''
    return longURL
    
class MainHandler(webapp.RequestHandler):
    
    def get(self, mode=""):
        
        if mode=='regist_favs':
            
            page = 1
            while page <= MaxPage:
                query = {'screen_name':T_ACCOUNT,
                        'include_entities':'True',
                        'page':page
                        }
                t_url = 'http://api.twitter.com/1/favorites.xml?%s' % urllib.urlencode(query)
                results = urlfetch.fetch(url=t_url, method=urlfetch.GET, deadline=10)
                dom = fromstring(results.content)
                            
                if results.status_code == 200:
                    if dom.findall('.//status'):
                        for e in dom.findall('.//status'):
                            q = favorites.gql("WHERE status_id = :1", e.findtext('./id'))            
                            fav = q.get()
                            if fav is None:
                                tweet_created = e.findtext('./created_at')
                                tweet_created = datetime.datetime.strptime(tweet_created, '%a %b %d %H:%M:%S +0000 %Y') \
                                                                + datetime.timedelta(hours=9)         
                                p = favorites(  status_id=e.findtext('./id'),
                                                text=e.findtext('./text'),
                                                created=tweet_created,
                                                url=e.findtext('./entities/urls/url/expanded_url'),
                                                user_id=e.findtext('./user/id'),
                                                user_screen_name=e.findtext('./user/screen_name'),
                                                )
                                if p.url == None:
                                    p.task = True
                                p.put()
                    page +=1
                                                            
            #task queue未登録のレコードを取り出す。
            fav = favorites.all().filter('task =', False).get()
                
            if fav:
                fav_values={'status_id':fav.status_id,
                            'user_screen_name':fav.user_screen_name,
                            'url':fav.url
                            }
                #タスクbookmarkTwFavのインスタンスを作成し、タスクを追加
                bookmark_task  = taskqueue.Queue('bookmarkTwFav')
                next_task = taskqueue.Task(url='/_bookmarkTwFav', params=fav_values)
                bookmark_task.add(next_task)
                
                #タスクretweetのインスタンスを作成し、タスクを追加
                retweet_task  = taskqueue.Queue('retweet')
                next_task = taskqueue.Task(countdown='60',url='/_retweet', params=fav_values)
                retweet_task.add(next_task)
                                  
                fav.task = True
                db.put(fav)
                
        if mode=='regist_GStars':
            #Googleスター付きアイテムをはてブする。
            results = urlfetch.fetch(url=GSTARURL, method=urlfetch.GET, deadline=10)
            dom = fromstring(results.content)  
            atom_ns = "http://www.w3.org/2005/Atom"       
            if results.status_code == 200:
                if dom.findall('.//{%s}entry' % atom_ns):
                    for e in dom.findall('.//{%s}entry' % atom_ns):
                        q = GStars.gql("WHERE id = :1", e.find('./{%s}id' % atom_ns).text)           
                        GStar = q.get()
                        if GStar is None:
                            for e2 in e.findall('.//{%s}source' % atom_ns):
                                source_title = e2.find('./{%s}title' % atom_ns).text
                            for e3 in e.findall('.//{%s}author' % atom_ns):
                                author = e3.find('./{%s}name' % atom_ns).text
                            p = GStars(id=e.find('./{%s}id' % atom_ns).text,
                                         title=e.find('./{%s}title' % atom_ns).text,
                                         link=e.find('./{%s}link' % atom_ns).attrib['href'],                                         
                                         source_title=source_title,
                                         author=author
                                        )
                            if p.link == None:
                                p.task = True
                            p.put()
            
            #task queue未登録のレコードを取り出す。
            GStar = GStars.all().filter('task =', False).get()
            
            if GStar:
                param={'title':GStar.title,
                       'source_title':GStar.source_title,
                       'author':GStar.author,
                       'link':GStar.link
                       }
                #タスクbookmarkGStarのインスタンスを作成し、タスクを追加
                bookmark_task  = taskqueue.Queue('bookmarkGStar')
                next_task = taskqueue.Task(url='/_bookmarkGStar', params=param)
                bookmark_task.add(next_task)
                
                #タスクtweetGStarのインスタンスを作成し、タスクを追加
                tweet_task  = taskqueue.Queue('tweetGStar')
                next_task = taskqueue.Task(url='/_tweet', params=param)
                tweet_task.add(next_task)
                
                GStar.task = True
                db.put(GStar)
                   
                            
    def post(self, mode=""):
        
        if mode=='_bookmarkTwFav':
            
            status_id = self.request.get("status_id")
            user_screen_name = self.request.get("user_screen_name")
            url = self.request.get("url")
            #WSSE認証
            hbc = HatenaBookmarkClient(HATENA_USERNAME,HATENA_PWD)
            hbc.getServiceURI() # サービスエンドポイントの取得(PostURIとFeedURI)
            bookmarkURL = get_longURL(url)
            if bookmarkURL <> '':
                status_url = 'http://twitter.com/%s/status/%s' % (user_screen_name,status_id)
                hbcomment = u'[☆]@%s %s' % (user_screen_name,status_url)
                hbc.postBookmark(bookmarkURL, hbcomment) # ブックマークの登録
                
        if mode=='_bookmarkGStar':
            
            url = self.request.get("link")
            #WSSE認証
            hbc = HatenaBookmarkClient(HATENA_USERNAME,HATENA_PWD)
            hbc.getServiceURI() # サービスエンドポイントの取得(PostURIとFeedURI)
            if url <> '':
                hbcomment = u'[☆]'
                hbc.postBookmark(url, hbcomment) # ブックマークの登録
                
        if mode=='_retweet':            
            status_id = self.request.get("status_id")
            
            #TwitterClientクラスの作成
            twc = oauth.TwitterClient(CONSUMER_KEY, CONSUMER_SECRET, 
                                          None)
            url='http://api.twitter.com/1/statuses/retweet/%s.json' % status_id            
            twc.make_request(url=url,
                            token=ACCESS_TOKEN, 
                            secret=ACCESS_TOKEN_SECRET, 
                            protected=True,
                            method='POST')
            
        if mode=='_tweet':            
            title = self.request.get("title")
            source_title = self.request.get("source_title")
            author = self.request.get("author")
            link = self.request.get("link")
            
            text = u'[☆] %s / %s (%s) %s' % (title,source_title,author,link)
            query={'status':text}
            
            #TwitterClientクラスの作成
            twc = oauth.TwitterClient(CONSUMER_KEY, CONSUMER_SECRET, 
                                          None)
            url='http://api.twitter.com/1/statuses/update.json'           
            twc.make_request(url=url,
                            token=ACCESS_TOKEN, 
                            secret=ACCESS_TOKEN_SECRET, 
                            protected=True,
                            additional_params=query,
                            method='POST')
            
def main():
    application = webapp.WSGIApplication([('/(.*)', MainHandler)],
                                         debug=True)
    util.run_wsgi_app(application)


if __name__ == '__main__':
    main()