import urllib
import hmac, sha
import time, random
# この関数の返り値を Authorization ヘッダーに設定する
def getOAuthHeader(url,method,params):
# 事前に確保した consumerKey, consumerSecret, accessToken, accessTokenSecret が設定されていること
oAuthParams = {
"oauth_consumer_key" : consumerKey,
"oauth_signature_method" : "HMAC-SHA1",
"oauth_timestamp" : str(int(time.time())),
"oauth_nonce" : str(random.getrandbits(64)),
"oauth_version" : "1.0",
"oauth_token" : accessToken
}
allParams=params.copy()
allParams.update(oAuthParams)
allParamList=[]
for key in sorted(allParams):
allParamList.append(key+"="+allParams[key])
msg=method+"&"+urllib.quote(url,"")+"&"+urllib.quote("&".join(allParamList),"~")
h=hmac.new(consumerSecret+"&"+accessTokenSecret,msg,sha)
oAuthParams["oauth_signature"]=h.digest().encode("base64").strip()
oAuthParamsList=[]
for key in oAuthParams:
oAuthParamsList.append(key+"=\""+urllib.quote(oAuthParams[key])+"\"")
return "OAuth "+(", ".join(oAuthParamsList))
import urllib, urllib2
import mimetypes
# 他の API でも使えるように汎用化した関数
def getAPIRequest(url,method,authentication,params={}):
multipart=False
newParams={}
for key in params:
value=params[key]
if value==None:
pass
elif isinstance(value,int):
newParams[key]=str(value)
elif isinstance(value,float):
newParams[key]=str(value)
elif isinstance(value,str):
newParams[key]=urllib.quote(value,"~")
elif isinstance(value,unicode):
newParams[key]=urllib.quote(value.encode("utf-8"),"~")
elif isinstance(value,tuple):
newParams[key]=value
multipart=True
params=newParams
url=url.encode("utf-8")
if method=="GET":
paramList=[]
for key in params:
paramList.append(key+"="+params[key])
request=urllib2.Request(url+"?"+"&".join(paramList))
else:
request=urllib2.Request(url)
if authentication:
# OAuth 対応のために必要なのはここだけ
if not multipart:
header=getOAuthHeader(url,method,params)
else:
header=getOAuthHeader(url,method,{})
request.add_header("Authorization",header)
if method=="POST":
if not multipart:
data=[]
for key in params:
data.append(key+"="+params[key])
request.add_data("&".join(data))
else:
BOUNDARY="---AntunBoundaryBoundaryBoundary"
data=""
for key in params:
data+="--"+BOUNDARY+"\r\n"
if not isinstance(params[key],tuple):
data+="Content-Disposition: form-data; name=\""+str(key)+"\""+"\r\n"
data+="\r\n"
data+=str(params[key])+"\r\n"
else:
fileType=mimetypes.guess_type(params[key][0])[0]
assert fileType!=None,"Could not determine file type"
data+="Content-Disposition: form-data; name=\""+str(key)+"\"; filename=\""+str(params[key][0])+"\""+"\r\n"
data+="Content-Type: "+str(fileType)+"\r\n"
data+="\r\n"
data+=str(params[key][1])+"\r\n"
data+="--"+BOUNDARY+"--"+"\r\n"
request.add_header("Content-Type","multipart/form-data; boundary="+BOUNDARY)
request.add_header("Content-Length",str(len(data)))
request.add_data(data)
else:
assert multipart==False
return request
# statuses/update 固有の処理はこんなにシンプル
def updateStatuses(tweet):
params={
"status" : tweet,
}
request=getAPIRequest("http://api.twitter.com/1/statuses/update.json","POST",True,params)
stream=urllib2.urlopen(request)
stream.read()
stream.close()
import json
def getUserStreams():
request=getAPIRequest("https://userstream.twitter.com/2/user.json","GET",True,{})
return urllib2.urlopen(request,timeout=90.0)
stream=self.twitter.getUserStreams()
response=u""
# 1行目のフォロワーリストを読み飛ばし (User Stream の場合だけ)
while True:
c=stream.read(1)
if c==u"\n":
break
response+=c
while True:
# readline() でも一応動きますが、仕様 (関数の実装) 上、正確なリアルタイムでレスポンスが得られなくなるので、read() で読んでます
response=u""
while True:
c=stream.read(1)
if c==u"\n":
break
response+=c
response=response.strip()
# 接続維持のために、時々空行が送られてくる
if response=="":
continue
response=json.loads(unicode(response),"utf-8")
print(json.dumps(response,indent=4,ensure_ascii=False))
| event キーの値 | イベント内容 | source | target | target_object | created_at | 備考 |
|---|---|---|---|---|---|---|
| follow | フォロー | フォローした人 | フォローされた人 | なし | フォローした時間 | |
| unfollow | アンフォロー | アンフォローした人 (=自分) | アンフォローされた人 | なし | アンフォローした時間 | 他人からされた時は来ない |
| user_update | 自分のプロフィールを更新した | プロフィールを更新した人 (=自分) | プロフィールが更新された人 (=自分) | なし | プロフィールを更新した時間 | |
| block | ブロック | ブロックした人 | ブロックされた人 | なし | ブロックした時間 | |
| unblock | ブロック解除 | (未確認) | (未確認) | (未確認) | (未確認) | |
| favorite | お気に入りに登録 | 登録した人 | 登録された人 | 登録されたツイート | 登録した時間 | |
| unfavorite | お気に入りから外す | 外した人 | 外された人 | 外されたツイート | 外した時間 | |
| list_created | 自分がリストを新規作成した | リストを作成した人 (=自分) | リストが作成された人 (=自分) | 作成されたリストの情報 | リストを作成した時間 | 非公開リストの情報はこない |
| list_destroyed | 自分がリストを削除した | リストを削除した人 (=自分) | リストが削除された人 (=自分) | 削除されたリストの情報 | リストを削除した時間 | 非公開リストの情報はこない |
| list_member_added | リストに追加 | リストに登録した人 | リストに登録された人 | 登録されたリストの情報 | リストに登録した時間 | 非公開リストの情報はこない |
| list_member_removed | リストから外す | リストから外した人 | リストから外された人 | 外された後のリストの情報 | リストから外した時間 | 非公開リストの情報はこない |
| list_updated | リストの情報が更新された | リストの情報を更新した人 | 更新されたリストの持ち主 (=source) | 更新されたリストの情報 | リストを更新した時間 | 非公開リストの情報はこない 他人のフォローしたリストの場合は未確認 |
| list_user_subscribed | リストをフォローした | リストをフォローした人 | リストをフォローされた人 | フォローされたリストの情報 | リストをフォローした時間 | |
| list_user_unsubscribed | リストをフォロー解除した | リストをフォロー解除した人 | リストをフォロー解除された人 | フォロー解除されたリストの情報 | リストをフォロー解除した時間 | |
| access_revoked | アプリ連携の許可を取り消した | 許可を取り消した人 | 許可を取り消されたアプリの作者 | 取り消されたアプリとトークンの情報 | 許可を取り消した時間 | 他人に取り消された場合は未確認 (たぶん来ない) |
| access_unrevoked | アプリ連携の許可を取り消しを止めた | 許可の取り消しを止めた人 | 許可の取り消しを止められたアプリの作者 | 取り消しを止めたアプリとトークンの情報 | 許可の取り消しを止めた時間 | 他人に取り消された場合は未確認 (たぶん来ない) |
| 識別方法 | 種類 | 備考 |
|---|---|---|
| retweeted_status キーがある | リツイート | タイムラインにリツイートを表示しない設定にしているユーザーのリツイートも流れてくる |
| direct_message キーがある | ダイレクトメッセージ | direct_message.sender.* から direct_message.recipient.* に direct_message.* のメッセージ |
| delete キーがある | ツイート削除 | delete.status.user_id が delete.status.id のツイートを消した リツイート元ツイートの削除、リツイートの取り消しもこれに含まれる (両者の区別は付かない) ダイレクトメッセージの削除はこないらしい |
| scrub_geo キーがある | 位置情報の削除 | scrub_geo.user_id が scrub_geo.up_to_status_id のツイートの位置情報を消した 設定画面の「全ての位置情報を削除する」で発生 |
| その他 | ツイート |
| エラー番号 | エラーメッセージ | 内容 |
|---|---|---|
| 34 | Sorry, that page does not exist | 指定された API が存在しない。 HTTP の 404 エラー相当。 API のアドレスが間違っていないか確認しましょう。 account/update_profile_image など問題修正までに時間が掛かる場合に、 一時的にこの状態にされていることがあります。 |
| 67 | Backend service is unavailable | Twitter のバックエンドシステムが落ちている ? |
| 88 | Rate limit exceeded | API の利用回数制限に達した。 HTTP の 429 エラー相当。 X-Rate-Limit-Reset ヘッダーで示された UTC 時間まで待ちましょう。 |
| 93 | This application is not allowed to access or delete your direct messages | 許可されていないダイレクトメッセージアクセス。 HTTP の 403 エラー相当。 My applications の アクセスレベル設定でダイレクトメッセージが許可されているか確認しましょう。 |
| 130 | Over capacity | Twitter のシステムが限界に達している。 HTTP の 503 エラー相当。 しばらく待ちましょう。 |
| 131 | Internal error | Twitter のシステムの原因不明の内部エラー。 HTTP の 500 エラー相当。 しばらく待ってみて、治りそうにない場合は Twitter API のサポート掲示板 で指摘しましょう。 |
| 135 | Could not authenticate you | OAuth の認証に失敗している。 HTTP の 401 エラー相当。 oauth_timestamp がずれていないか、Authorization ヘッダーの生成が間違っていないか見直しましょう。 |
| 150 | You cannot send messages to users who are not following you | ダイレクトメッセージ指定のツイートができない。 HTTP の 403 エラー相当。 ツイートの頭に特定のテキストがあるとコマンドとして認識 されますが、「D フォローされてない人」や「M フォローされてない人」を 使おうとした時にこのエラーになります。 ツイートの頭の文を変えましょう。 |
| 185 | User is over daily status update limit | 1日のツイート数制限に達した。 HTTP の 403 エラー相当。 しばらく待ちましょう。 88, 191 番のようには、復旧時間は分かりません。 公式サポート によると、2〜3時間待つべきです。 |
| 186 | Status is over 140 characters | ツイートが 140 文字を超えている。 HTTP の 403 エラー相当。 ツイートを 140 文字以下に抑えましょう。 |
| 187 | Status is a duplicate | 重複ツイートしようとしている。 HTTP の 403 エラー相当。 すでにツイート済みのテキストを送っているので、ツイートをやめましょう。 |
| 189 | Error creating status | 原因不明。 HTTP の 403 エラー相当。 statuses/update_with_media でまれに発生。 少し間を置いてもう一度 API を呼び出すと成功するようです。 |
| 190 | Status creation failed: ツイートが長すぎます. | ツイートが長すぎる。 HTTP の 403 エラー相当。 ツイート中に URL が含まれており t.co に変換した結果、140 文字を超えるとおそらく 186 番ではなく、こちらが出るのだと思われます。 URL 込みのツイートを 140 文字以下に抑えましょう。 |
| 191 | User アカウント名 is over daily photo limit | 画像アップロード数の制限に達した。 HTTP の 403 エラー相当。 X-MediaRateLimit-Reset ヘッダーで示された UTC 時間まで待ちましょう (ツイート数制限に引っかかった時は復旧時間は分からない)。 |
| 215 | Bad Authentication data | OAuth 認証に失敗した。 HTTP の 400 エラー相当。 リクエストの Authorization ヘッダーを正しくしましょう。 |
戻る