twitter用のボット"twnvls"を書いてみた。
http://twitter.com/twnvlsこれはtwitter上でリレー小説をつくるというもので、
投稿者が@twnvls宛にポストした内容を拾って
twnvls名義でポストする。
(当然オリジナルの投稿者もわかるようにする)
一日分のポストはLoudTwitterでまとめて
はてなダイアリーに残す。
初めたばかりなのでまだポストは全然ないが^^;
まぁゆるゆると見守って行きましょうかねと。
作り的には、丁度今GAEでPythonを使ってるので
Python用のtwitterモジュールを使用した。
このモジュールを使うにはsimplejsonも必要。
使い方はいたって簡単。
import simplejson
import twitter
api = twitter.Api(username='foo', password='bar')
replies = api.GetReplies()
これで'foo'宛のリプライが取得できる。
タイムラインを取得したければ、
user_timeline = api.GetUserTimeline()
public_timeline = api.GetPublicTimeline()
とやればよい。
気をつけないといけないのは
GetReplyメソッドでは新しいリプライから順に取得されること。
今回は古い順に処理したいのでソートしたいが、
apiには取得順を変更するインタフェースはないので
アプリ側で対処する必要がある。
Pythonでは辞書型リスト(いわゆる連想配列/Perlで言うところのハッシュ)を
スタックとして使えるので、今回はそれを使うことにする。
stack = []
for r in replies:
p = []
p.append(r.id)
p.append(r.user.GetScreenName())
p.append(r.text)
stack.append(p)
これでstackには新>旧の順で格納される。
ポストする時にはstackからpopすれば旧>新の順で取り出せる。
MAX_CHAR = 140 # twitterの文字数制限
i = 0
length = len(stack)
while i < length:
s = stack.pop()
id = s[0]
user = s[1]
text = s[2][len('@foo'):].lstrip() # リプライの先頭にある"@foo"と文頭のスペースを削除
postuser = ' [@' + user + ']'
posttext = text[0:MAX_CHAR-len(postuser)] + postuser # MAX_CHAR内で収まるよう成形
status = api.PostUpdate(posttext)
i = i + 1
基本的にはこれでできる。
あとはGetReplyメソッドがその時点の最新20件を返すので
同じリプライを二重投稿してしまう可能性があり、それを防止しないとならない。
やり方はいろいろあるだろうが、
今回はPostUpdateした時点でid等をアーカイブしておき(これを便宜上アーカイブファイルと呼ぶ)、
GetReplyした時にアーカイブファイルと突き合わせ、
アーカイブ内にあれば既に投稿済みと判断してスキップすることにした。
apiを使ってチェックする方法もあるが、
twitterのapi制限にひっかかりそうだし、twitter側にも負荷をかけるので今回はやめた。
アーカイブファイルが大きくなるとレスポンスが悪化する恐れがあるが、
アーカイブを整理するなどして対策はできるだろうと。
と、言う訳で完成版がこれ。
# coding:utf-8
import sys
import os
import simplejson
import twitter
max_char = 140 # Max character length of twitter api
arcfilename = ".twnvls_archive"
try:
arcpath = os.environ["TWNVLS_ARCHIVE_DIR"] + arcfilename
except:
arcpath = os.path.join(os.path.dirname(__file__),arcfilename)
# Open archive file
try:
fa = open(arcpath, "a+")
farc = fa.read() # Once read all of archive file
except:
sys.stderr.write("IOError:File %s can't open" % arcpath)
sys.exit()
# Constract twitter api instance
USERNAME = "username"
PASSWORD = "password"
api = twitter.Api(username=USERNAME, password=PASSWORD)
# Get replies
try:
replies = api.GetReplies()
except:
sys.stderr.write("TwitterApiError:User %s can't get replies" % USERNAME)
sys.exit()
# Make stack(list)
# NOTE : replies is sorted from newer to older.
# So, to post twitter, 1st,Make stack. 2nd,Pop from stack.
stack = []
for r in replies:
try:
# Skip already posted replies (by checking archive file)
farc.index("%s %s" % (r.id, r.created_at_in_seconds))
continue
except:
pass
p = []
p.append(r.id)
p.append(r.created_at_in_seconds)
p.append(r.created_at)
p.append(r.user.GetScreenName())
p.append(r.text)
stack.append(p)
# Post twitter
i = 0
length = len(stack)
while i < length:
s = stack.pop()
id = s[0]
created_at_in_seconds = s[1]
created_at = s[2]
user = s[3]
text = s[4][len("@"+USERNAME):].lstrip() # Delete reply name "@~" & left space
postuser = " [@" + user + "]"
posttext = text[0:max_char-len(postuser)] + postuser
status = api.PostUpdate(posttext)
# Add to archive file
arctext = "%s %s %s %s\n" % (id, created_at_in_seconds, created_at, user)
fa.write(arctext)
i = i + 1
fa.close()
今のところは動いてるみたい^^;
今後は自動フォロー機能でも追加しようかな。