Twitter API を使ってみる


Twitter API の使い方などの説明です (2011/4/12 新規作成) (2013/6/13 API 1.1 対応版に書き直し) (2018/8/23 Account Activityを追加)。

API / 種類 / 認証 / REST API / Streaming API / User Streams / Account Activity / エラー / 戻る / トップページ


Twitter API

Twitter は、 ユーザーが「ツイート」と呼ばれる 140 文字のつぶやきを投稿し、 閲覧できるコミュニケーション・サービスです。
 
Twitter API は、そのツイートの変更や参照・検索をプログラムから 扱えるようにする公式の API サービスです。 世の中に数多ある Twitter クライアントや、連携サービスも Twitter API を使って作成されており、相当のことができます。
 
Twitter API の解説をしている Web ページはそれなりにありますが、 API の仕様や周辺事情が色々と変化しているため、2013/6/13 時点で 私が調べた情報をまとめなおしたものをここに載せようと思います。 現在は API のバージョンが 1.1 に完全移行しているため、 ここでの説明も API 1.1 に準拠しています。

API の種類

Twitter API のドキュメント類は本家サイトで提供されています。 API Documentation dev.twitter.com がそのサイトです。 以前は Wiki ページで管理されていましたが、こちらのサイトが最新です。
 
Twitter API は大きく分けて、 REST APIStreaming API、 の2種類があります (API 1.0 の頃は Search API もありましたが、 API 1.1 で REST API にまとめられました)。 REST API はツイートの更新や参照を行う最も基本的な API で、 URL にクエリーを渡してレスポンスを得る REST を使います。 Streaming API は比較的新しい API で、タイムラインの変更をリアルタイムに 受け取れるものです。 これも実際には REST でアクセスしますが、タイムラインが更新されるまで レスポンスを返すのを保留させる (ロングポーリングと呼びます) ことで、 リアルタイム性を確保しています。
 
REST API には 15 分間に同じ人の同じアプリで API 毎に呼び出し回数に制限があります。 API 1.0 の頃は人ごとの制限でしたが API 1.1 になってアプリごと・APIごとに緩和されているため、 特定のアプリや API が使用回数を使い尽くしても 他のアプリに影響を及ぼしません。
 
またこれら以外に、API ではないのですが、 自分の Web ページに Twitter 関係の フォローボタン・ツイートボタンを載せたいのであれば ツイートボタン から ブログパーツ (スクリプト) を入手するだけで目的が達成できます。 ウィジェットを載せたいのであれば ウィジェット から ブログパーツ (スクリプト) が入手できます (入手するには 自分のアカウントでログイン必要)。 また、さらに 2011/4 から Web Intents という 自分の Web ページから、 ツイートする・返信する・リツイートする・お気に入りにする、 を開くリンクを貼る仕組みも公開されました。 これらでやりたいことが達成できるのであれば、これらを使う方が簡単です。 他にJavaScript でフォローする・ツイートするパーツを Web ページに 埋め込む簡易ライブラリ @anywhere がありましたが、これは廃止されています。

認証

Twitter API は認証を必要とします。 従来 Basic 認証や認証無しで使える API もありましたが、 セキュリティ強化のため、現在では OAuth 認証が必須となっています。 Basic 認証はすでに廃止されているので、面倒でも OAuth 認証を使いましょう。
 
Twitter API で OAuth 認証を使うには、 Consumer key, Consumer secret の取得が必要です。 また、BOT のように、クライアントアプリケーションから 特定のアカウントを操作する場合は、 Access Token, Access Token Secret の準備も必要になります。 ここでは、BOT から Twitter API を扱う場合について説明します。
 
Consumer key, Consumer secret の取得のために、 開発者ページでアプリケーションを登録します。 Twitterアプリケーション の ページに自分の Twitter アカウントでログインし (BOT を作成する場合、 後述する Access Token の情報をすぐ得られるように BOT のアカウントで ログインした方が楽です)、 「新しいアプリケーションを登録する」ボタンから 作成するアプリケーションの情報を入力して登録します。 この時、BOT 自体は単体のプログラムとして動作するので 「アプリケーションの種類」は「クライアントアプリケーション」を、 「標準のアクセスタイプ」はツイートを発する (データを書き込む) ので 「Read & Write」を選びます。 正しい情報を入力して「アプリケーションを登録する」ボタンを押すと、 すぐに登録は完了し、 Consumer key, Consumer secret が表示されている画面がでるはずです。
 
Access Token, Access Token Secret はアカウント毎の情報なので、 BOT の場合は BOT のアカウントでログインして、 さきほどのアプリケーション情報の画面の右に表示されている 「My Access Token」ボタンを押すと、参照できます。 以前はここも自分でプログラムを書いて取得する必要がありましたが、 今は Web 上で簡単に参照できるようになってます。
 
OAuth 認証は複雑ですが、クライアントアプリケーションの場合は API にアクセスする際に、ヘッダー名 Authorization に必要情報を 詰め込めば認証できます。 その情報を作るのが手間ですが、ライブラリを利用するなりすれば良いでしょう。 ここでは、私が作った Python のヘッダー生成コードを載せておきます。 以後のコードは全て仕組みを理解するために、 Python の標準機能のみを使ってます。
	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))
 
シグネチャ oauth_signature の生成が一手間です。 HTTP リクエストメソッド (GET か POST か) と、リクエスト URL、 API に送るパラメータ (ここでは引数 params に渡される辞書) の アルファベット順ソート、を URI エスケープしつつ記号 & で接続し、 その文字列を HMAC-SHA1 によってダイジェスト値を生成し、 BASE64 でエンコードします。 シグネチャの key は Consumer secret と Access Token Secret を & 記号で接続したものです。 この時、oauth_ で始まる OAuth 用のパラメータをいくつか含ませます。
 
この生成情報をミスしていると (認証に失敗すると)、 { "error" : "Incorrect signature" } とのエラーメッセージが返ってきます。 空白文字は + ではなく %2b に、~ (チルダ) はそのまま ~ に エスケープするのが正解のようです。
 
なお、アイコンの更新などでマルチパートでリクエストを送信することが ありますが、その場合は API に送るパラメータにはそれらの情報は 含みませんのでご注意下さい (私はそこで苦労しました...)。

REST API 基本パターン

まずは REST API から。 ドキュメントを参照して、HTTP リクエストメソッドが GET か POST か、 必須パラメータは何かを確認します。
 
例えば、ツイートをする API は statuses/update で各項目を確認すると、 "Resource Information" の "HTTP Methods" から POST でのアクセスで、 "Parameters" の "Required" から必須パラメータは status (ツイートする内容を書くところ) と分かります。
	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("https://api.twitter.com/1.1/statuses/update.json","POST",True,params)
		stream=urllib2.urlopen(request)
		stream.read()
		stream.close()
一般的な REST のように、必要なパラメータ status を渡して API の URL に アクセスするだけです。ここでは POST アクセスなので、パラメータは URL にではなく、POST データで渡します。 なお、ほぼ全ての API で URL の末尾を .json にすれば JSON で、 .xml にすれば XML でレスポンスが得られます。 ただし API ごとにどのフォーマットが指定可能か決まっているので、 注意しましょう (.json のみのものがあるので、 JSON を使うのがベターなようです)。
 
また、パラメータで日本語を扱う場合は UTF-8 で扱います。
 
文字数のカウントはコードポイント数、ただし英語 (厳密には [\u0000-\u10FF\u2000-\u200D\u2010-\u201F\u2032-\u2037] の範囲) は0.5文字です。 サロゲートペア(例:🐲(U+1F432 または U+D83D U+DC32))も1文字です。 異体字(例:葛󠄁(U+845B U+E0101))はこの例だと2文字カウント (普通の漢字+異体字セレクタ)です。
 
2018/10/11から絵文字は見た目上の文字数に変更されています。 例えば、ゼロ幅接合子で作られた文字(例:👨‍👩‍👦‍👦(U+1F468 U+200D U+1F469 U+200D U+1F466 U+200D U+1F466))でも1文字カウント、 絵文字シークエンス各種(例:1️⃣(U+0031 U+FE0F U+20E3), 👏🏽(U+1F44F U+1F3FD), 🇯🇵(U+1F1EF U+1F1F5))も1文字カウントです。 絵文字スタイル化する異体字セレクタEPVS(U+FE0F)を適用しても(例:⭐️(U+2B50 U+FE0F))1文字カウントですが、 テキストスタイル化する異体字セレクタTPVS(U+U+FE0E)を適用すると(例:⭐︎(U+2B50 U+FE0E))絵文字ではなくなるため2文字カウントになるので注意です。
 
API の Entity の indices で示されるインデックスでは コードポイント数で数え、英語等も1文字扱いになります。

Streaming API 基本パターン

次に Streaming API。 Streaming API は、基本は REST API と同じで、 タイムラインの変更をリアルタイムに受け取るために、 タイムラインが更新されるまでレスポンスが返ってこない点が違います。 一度に何本も接続できない、切断と接続を短期間に連続できないなどの 制限はあるものの、Streaming API は、 API の呼び出し回数制限がかからない利点があります。
 
Streaming API でおそらく一番良く使われるのは User Streams で、 認証したユーザーのタイムラインの内容+αが流れます ( ちなみに指定のユーザーや検索語を流したい場合は statuses/filter を使います)。
	import json
	
	def getUserStreams():
		request=getAPIRequest("https://userstream.twitter.com/1.1/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))
処理自体は REST API と同様に OAuth で認証しつつ、 コネクションを張ってレスポンスを待ちます。 レスポンスが長期間保留されるため、タイムアウト処理をしないように 呼び出し側の処理を気をつけなければいけません。 また、API のアドレスのサブドメインが 〜stream になっている点に注意です。
 
なお、全てのツイートを対象とする、statuses/firehose, Site Streams は ごく一部の限られた開発者にのみ提供されており、通常は使えません。

User Streams

2018/8/16をもって、User Streams機能は廃止されました。 タイムラインをリアルタイムに受信する代替機能はありません。 通知は一部制限付きですが、Account Activity APIで代替できます。
 
User Streams は他の Streaming API とは違い、ツイート以外にも、 自分がした、または自分を対象に他人がした、 リツイートやお気に入り登録・ツイートの削除などもリアルタイムに 流れてくる点が特殊です。
 
接続すると、一行目にフォロー相手のユーザー ID のリスト ({"friends":[1497,169686021,...]} のような JSON 形式) がまず返ってきます。 以後は、タイムラインにツイートが流れてくるか、 お気に入り登録などのイベントが起こるまではレスポンスは保留されます。 ただし、接続維持のために時々空行が送られてきますので、 逆に 90 秒以上何も返ってこない場合は切断する処理も必要です。
 
通常のタイムラインに流れるようなツイート/リツイート情報は、 API の statuses/home_timeline と 同じ形式で 1 ツイートごとに返ってきます。 お気に入り登録などのイベントの場合は、 "event" (イベントの種類)、"source" (イベントを引き起こした人)、 "target" (イベントの対象の人)、 "target_object" (人以外のイベント対象の情報)、 "created_at" (イベントが起こされた時間) の キーを持った情報が返ってきます (イベントの種類ごとに どれが返ってくるかは違う)。 そのためまず "event" キーを探して、 あれば各イベントとして、 なければツイートとして処理します。
 
イベントの種類
event キーの値 イベント内容 source target target_object created_at 備考
follow フォロー フォローした人 フォローされた人 なし フォローした時間
unfollow アンフォロー アンフォローした人 (=自分) アンフォローされた人 なし アンフォローした時間 他人からされた時はこない
user_update 自分のプロフィールを更新した プロフィールを更新した人 (=自分) プロフィールが更新された人 (=自分) なし プロフィールを更新した時間
user_delete アカウントが消された アカウントが消された人 アカウントが消された人 "client_application" の文字列 アカウントが消された時間 アカウント復活はこない
2014/4/9頃から導入
user_suspend アカウントが凍結された アカウントが凍結された人 アカウントが凍結された人 "client_application" の文字列 プロフィールを更新した時間 アカウント凍結解除はこない
2014/4/9頃から導入
block ブロック ブロックした人 ブロックされた人 なし ブロックした時間
unblock ブロック解除 (未確認) (未確認) (未確認) (未確認)
mute ミュート ミュートした人 ミュートされた人 (未確認) ミュートした時間 他人からされた時はこない
2014/7/23以降の近いうちに導入
unmute ミュート解除 ミュート解除した人 ミュート解除された人 (未確認) ミュート解除した時間 他人からされた時はこない
2014/7/23以降の近いうちに導入
favorite お気に入りに登録 登録した人 登録された人 登録されたツイート 登録した時間
unfavorite お気に入りから外す 外した人 外された人 外されたツイート 外した時間
favorited_retweet 自分のリツイートがお気に入りに登録された 登録した人 登録された人 (=自分) 登録されたツイート 登録された時間 公式には告知・ドキュメントなし
2014/11/11頃に導入
retweeted_retweet 自分のリツイートがリツイートされた リツイートした人 リツイートされた人 (=自分) リツイートされたツイート リツイートされた時間 2015/1/20から順次導入
quoted_tweet 自分のツイートが引用リツイートされた 引用リツイートした人 引用リツイートされた人 (=自分) 引用リツイート 引用リツイートされた時間 2015/5/27の数週間後から導入
list_created 自分がリストを新規作成した リストを作成した人 (=自分) リストが作成された人 (=自分) 作成されたリストの情報 リストを作成した時間 非公開リストの情報はこない
list_destroyed 自分がリストを削除した リストを削除した人 (=自分) リストが削除された人 (=自分) 削除されたリストの情報 リストを削除した時間 非公開リストの情報はこない
list_member_added リストに追加 リストに登録した人 リストに登録された人 登録されたリストの情報 リストに登録した時間 非公開リストの情報はこない
list_member_removed リストから外す リストから外した人 リストから外された人 外された後のリストの情報 リストから外した時間 非公開リストの情報はこない
list_updated リストの情報が更新された リストの情報を更新した人 更新されたリストの持ち主 (=source) 更新されたリストの情報 リストを更新した時間 非公開リストの情報はこない
他人のフォローしたリストの場合は未確認
list_user_subscribed リストをフォローした リストをフォローした人 リストをフォローされた人 フォローされたリストの情報 リストをフォローした時間
list_user_unsubscribed リストをフォロー解除した リストをフォロー解除した人 リストをフォロー解除された人 フォロー解除されたリストの情報 リストをフォロー解除した時間
access_revoked アプリ連携の許可を取り消した 許可を取り消した人 許可を取り消されたアプリの作者 取り消されたアプリとトークンの情報 許可を取り消した時間 他人に取り消された場合は未確認 (たぶんこない)
access_unrevoked アプリ連携の許可を取り消しを止めた 許可の取り消しを止めた人 許可の取り消しを止められたアプリの作者 取り消しを止めたアプリとトークンの情報 許可の取り消しを止めた時間 他人に取り消された場合は未確認 (たぶんこない)
 
ツイートとして処理する場合も以下の 5 パターンがあるようです。
 
ツイートの種類
識別方法 種類 備考
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 のツイートの位置情報を消した
設定画面の「全ての位置情報を削除する」で発生
その他 ツイート
 
User Streams 以外の Streaming API でも、delete は流れてくるようです。

Account Activity API

Account Activity APIは、ユーザーの通知とダイレクトメッセージを リアルタイムに受信するための機能です。 他のAPIとは違い、自分で立ち上げたサーバーのURLを登録して 呼んで貰うことで機能を実現します。 ここでは無料で15ユーザーまで使える Premium Account Activity APIを取り扱います。
 
Account Activity APIではまずWebページ上で Account Activityの利用設定をした専用のアプリを用意します。 そして、そのアプリに対してWebhook(受信するURL)を登録します。 その後、通知を受け取って欲しいアカウントから自分を購読登録しにいきます。 Webhookの登録と購読登録はAPIを介して行いますが、 主語が違う点に注意して下さい。 使うアカウントと認証方法が違い、間違えたときのエラーメッセージが その指定ミスだと分かりづらい表示になっているので、 正しく理解していないと苦労します。
 
アプリ情報の登録
 
順番にいきましょう。まず、専用のアプリの用意です。 まず2018年中頃から始まった新しい 開発者登録 をします。 従来の審査不要のままでは使えません。 英文で具体的な利用目的を説明し、審査を合格する必要があります。 審査が通ったらそのアカウントでアプリを作成します。 Account ActivityではWebhookを登録するためにwrite属性が、 ダイレクトメッセージも受信するためdirect message属性が必要になります。 そのため、パーミッションは必ず "Read, write, and direct messages" になります。 この設定が間違っていると、あとのAPIコール時にエラーになりますが、 パーミッションが間違っている旨のエラーメッセージは出してくれないため、 注意が必要です。
 
アプリを作成したら Dev environments のページの Account Activity API の項目から アプリを作成したアカウントと、アプリを、 この後使う "Dev environment label" (以後 env_name と呼びます) をつけて関連付けます。 これで、Account Activity APIが使えるアプリが準備できます。 以後どの認証でもこのアプリの Consumer key, Consumer secret を使うことになります。
 
Webhookの登録
 
まず、Webhookの操作です。Webhookはアプリに対しての操作なので、 ユーザー情報のないApplication-only OAuth(詳しくは後述します)認証を 使うのが良いのですが、Application-only OAuthは読み込み専用なので、 書き込み系のAPIを使うために通常の認証を使う必要があります。 先ほど作成した特別なアプリのConsumer key, Consumer secretを使った、 アプリを作成したアカウントのAccess Tokenであれば一通り使えます。 つまり、アプリ管理画面で取得できるAccess Tokenで問題ありません。
 
Webhookを扱うAPI
API (https://api.twitter.com/1.1/) 内容 認証
POST account_activity/all/env_name/webhooks.json Webhookを登録して、妥当性チェック アプリを作成したアカウント
PUT account_activity/all/env_name/webhooks/webhook_id.json 登録済みのWebhookの妥当性チェック アプリを作成したアカウント
GET account_activity/all/webhooks.json Webhookの登録状況を取得 アプリを作成したアカウント or Application-only OAuth
GET account_activity/all/env_name/webhooks.json Webhookの登録状況を取得 アプリを作成したアカウント or Application-only OAuth
DELETE account_activity/all/env_name/webhooks/webhook_id.json 登録済みのWebhookを解除 アプリを作成したアカウント
 
なお、APIのアドレスにあるenv_nameは、アプリ登録時に "Dev environment label" に書いた名前です。 このようにほとんどのAPIで指定に使います。
 
POSTのAPIで1回登録すれば、止めるか異常が発生するまで動作し続けます。 登録するWebhookがアプリの持ち主の管理する正しいURLであることを 確認するためにチェック機構が働きます。これをパスしないと登録に失敗します。 チェックに失敗したり、サーバー不安定でWebhookが止まってしまったときは PUTで再確認を促せます。 妥当性チェックは登録後も24時間おきに行われるようです。
 
妥当性チェックはWebhookとして登録したURLに引数 crc_token 付きの GETアクセスが来るため、下記コードのようにConsumer secretを使って sha256を算出してJSON形式で返します。
	import json
	import urlparse
	import base64
	import hashlib
	import hmac
	import SocketServer
	import BaseHTTPServer
	
	class Server(SocketServer.ThreadingMixIn,BaseHTTPServer.HTTPServer):
		allow_reuse_address=True
	
	class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
		protocol_version="HTTP/1.1"
		
		def do_GET(self):
			query=urlparse.urlparse(self.path).query
			query=dict(map(lambda arg:(arg.split("=",1)+[None])[:2],query.split("&")))
			
			if "crc_token" in query:
				crcToken=query.get("crc_token")
				
				# 事前に確保した consumerSecret が設定されていること
				
				response={
					"response_token" : "sha256="+
					    base64.b64encode(hmac.new(
					    consumerSecret.encode(),msg=crcToken.encode(),digestmod=hashlib.sha256
					    ).digest()).decode(),
				}
				response=json.dumps(response)
				
				self.send_response(200)
				self.send_header("Content-type","application/json")
				self.send_header("Content-Length",str(len(response)))
				self.end_headers()
				
				self.wfile.write(response)
				self.wfile.flush()
	
	server=Server(("localhost",8080),Handler)
	server.serve_forever()
購読登録
 
購読登録は通知を受け取って欲しいアカウントからAPIを呼び出します。 そのAccess Tokenは、先ほど作成した特別なアプリの Consumer key, Consumer secretに対して得たものである必要があります。 購読したいアカウントごとにPOSTのAPIを順番に呼んでいきましょう。
 
Subscriptionを扱うAPI
API (https://api.twitter.com/1.1/) 内容 認証
POST account_activity/all/env_name/subscriptions.json 自分を購読対象として登録 購読するアカウント
GET account_activity/all/env_name/subscriptions.json 自分が購読対象として登録済みか確認 購読するアカウント
DELETE account_activity/all/env_name/subscriptions.json 自分を購読対象から解除 購読するアカウント
 
現在購読中の一覧や数を取得するAPIは、購読するアカウント個々ではなく、 アプリに結びついている情報なのでApplication-only OAuth認証が必須です。 Webhookを扱うAPIと同様にアプリを作成したアカウントの認証でも良さそうに 思えますが、 こちらはなぜかApplication-only OAuth以外は受け付けない厳しさになってます。
 
普段のOAuth認証はサービスプロバイダ(Twitter)、 コンシューマー(アプリを作成しているあなた)、 ユーザーの3者が関わる3-legged OAuth方式ですが、 Application-only OAuthはサービスプロバイダとコンシューマーのみ の2者が関わる2-legged OAuth方式です。 ユーザー情報がないため、タイムラインなどユーザーに結びついた (ログインしていないと見えない)情報へアクセスできない安全性が確保されます。
 
TwitterのOAuthは基本OAuth1.0ベースですが、 Application-only OAuthだけはOAuth2.0に基づいています。 OAuth2.0はOAuth1.0とは後方互換性はなく、 よりシンプルに扱えるように再設計されています。
 
Application-only OAuthの認証の仕方は簡単です。 Consumer key, Consumer secretをID,パスワードに見立ててBasic認証で ”grant_type=client_credentials” を https://api.twitter.com/oauth2/token に POST 呼び出しをすると、 access_token が得られます (Access Token Secretはありません)。 あとは、APIを呼び出す際に "Authorization: Bearer access_token" を付与するのみです。
 
Subscriptionを扱うAPI
API (https://api.twitter.com/1.1/) 内容 認証
GET account_activity/all/env_name/subscriptions/list.json アプリに登録済みの購読対象の一覧を取得 Application-only OAuth
GET account_activity/all/subscriptions/count.json アプリに登録済みの購読対象数を取得 Application-only OAuth
 
通知の受信
 
購読登録後は通知が発生すると、Webhookとして登録したURLが 逐次POSTアクセスで呼ばれるようになります。
 
具体的な情報の形式は Account Activity Data Objects を参照して下さい。 "direct_message_events"イベントの記述は間違っており、"apps"はなく、 "direct_message_indicate_typing_events"イベント と同様に"users"がありますので、ご注意を。
 
ツイートの形として通知内容を表現できるもの(リツイートや引用ツイート)は "tweet_create_events"イベントで流れてきます。 自分のツイートと、通知対象となるツイートだけで、 フォロー相手のツイートがくるわけではありません。
 
"mute_events"イベントはtypeの値がunmuteで自分のミュート解除も通知されます。 "block_events"(type=unblock), "follow_events"(type=unfollow)イベントも同様です。 "follow_events"(type=unfollow)は自分のフォロー解除のみで、 被フォロー解除は届きません。
 
Twitter APIで得られるEntity(<Tweet Object>や<User Object>)では 通常"id_str"で文字列型(桁あふれ防止のため)を返しますが、 follow_events, block_events, mute_events, direct_message_events系 イベントではユーザーのIDを"id"で文字列型を返してくるので、注意が必要です。 "tweet_create_events"イベントは従来通り "id"は数値型、"id_str"が文字列型です。

エラー処理

エラーが発生した場合、API は HTTP のステータス番号と同時に レスポンスで詳しい情報を返してくれます
 
詳細なエラー内容は {"errors":[{"message":"Sorry, that page does not exist","code":34}]} のような JSON 形式で返ってきます。 エラー番号は Error Codes & Responses Twitter Developers に一覧がありますが、実際にはもっと種類があるようです。
 
エラー番号
エラー番号 HTTP ステータスコード エラーメッセージ 内容
34 404 Sorry, that page does not exist 指定された API が存在しない。 API のアドレスが間違っていないか確認しましょう。 account/update_profile_image など問題修正までに時間が掛かる場合に、 一時的にこの状態にされていることがあります。
44 400 attachment_url parameter is invalid. attachment_url には引用リツイートするツイートの URL しか渡せません。
50 404 User not found. ダイレクトメッセージ指定で存在しないユーザーを指定している。 ツイートの頭に特定のテキストがあるとコマンドとして認識 されますが、「D 存在しない人」や「M 存在しない人」を 使おうとした時にこのエラーになります。 ツイートの頭の文を変えましょう。
67 Backend service is unavailable Twitter のバックエンドシステムが落ちている ?
68 410 The Twitter REST API v1 will soon stop functioning. Please migrate to API v1.1. https://dev.twitter.com/docs/api/1.1/overview. 廃止された Twitter API 1.0 を呼んでいる。 Twitter API 1.1 に移行しましょう。
88 429 Rate limit exceeded API の利用回数制限に達した。 X-Rate-Limit-Reset ヘッダーで示された UTC 時間まで待ちましょう。
89 401 Invalid or expired token Bearer token が正しくない。 認証の指定が間違ってないか、 Bearer token が期限切れになってないか確認しましょう。
93 403 This application is not allowed to access or delete your direct messages 許可されていないダイレクトメッセージアクセス。 My applications の アクセスレベル設定でダイレクトメッセージが許可されているか確認しましょう。
99 403 Unable to verify your credentials OAuth2 Bearer Token の認証に失敗している。 Basic 認証の指定が間違っていないか見直しましょう。
130 503 Over capacity Twitter のシステムが限界に達している。 しばらく待ちましょう。
131 500 Internal error Twitter のシステムの原因不明の内部エラー。 しばらく待ってみて、治りそうにない場合は Twitter API のサポート掲示板 で指摘しましょう。
135 401 Could not authenticate you OAuth の認証に失敗している。 oauth_timestamp がずれていないか、Authorization ヘッダーの生成が間違っていないか見直しましょう。
150 403 You cannot send messages to users who are not following you ダイレクトメッセージ指定のツイートができない。 ツイートの頭に特定のテキストがあるとコマンドとして認識 されますが、「D フォローされてない人」や「M フォローされてない人」を 使おうとした時にこのエラーになります。 ツイートの頭の文を変えましょう。
185 403 User is over daily status update limit 1日のツイート数制限に達した。 しばらく待ちましょう。 88, 191 番のようには、復旧時間は分かりません。 公式サポート によると、2〜3時間待つべきです。
186 403 Status is over 140 characters ツイートが 140 文字を超えている。 ツイートを 140 文字以下に抑えましょう。
187 403 Status is a duplicate 重複ツイートしようとしている。 すでにツイート済みのテキストを送っているので、ツイートをやめましょう。
188 403 Status contains malware. ツイートに含まれるリンク先が不正サイトと判定されている。
189 403 Error creating status 原因不明。 statuses/update_with_media でまれに発生。 少し間を置いてもう一度 API を呼び出すと成功するようです。
190 403 Status creation failed: ツイートが長すぎます. ツイートが長すぎる。 ツイート中に URL が含まれており t.co に変換した結果、140 文字を超えるとおそらく 186 番ではなく、こちらが出るのだと思われます。 URL 込みのツイートを 140 文字以下に抑えましょう。
191 403 User アカウント名 is over daily photo limit 画像アップロード数の制限に達した。 X-MediaRateLimit-Reset ヘッダーで示された UTC 時間まで待ちましょう (ツイート数制限に引っかかった時は復旧時間は分からない)。
215 400 Bad Authentication data OAuth 認証に失敗した。 リクエストの Authorization ヘッダーを正しくしましょう。
220 403 Your credentials do not allow access to this resource 許可されていない API を呼んだ。 Bearer token では使えない API を呼んだか、 公式アプリにだけ許されている隠し API を呼んだので、使えません。
226 403 This request looks like it might be automated. To protect our users from spam and other malicious activity, we can't complete this action right now. Please try again later. SPAM 判定された。 おそらくほとんど同じ内容を非常に短時間に連続した時に。 メッセージにあるように、しばらく後にツイートしましょう。
324 400 User id ユーザーID doesn't own media id メディアID 他人がアップロードしたメディアを添付しようとしている。
326 403 To protect our users from spam and other malicious activity, this account is temporarily locked. Please log in to https://twitter.com to unlock your account. アカウントハック判定された。 アカウントのメールアドレスにパスワードリセットのお知らせが 届いているはずなので、しかるべき対応をしましょう。
420 Exceeded connection limit for user Streaming API の接続が多すぎる。適度に抑えましょう。 このエラーだけレスポンスのメッセージが JSON 形式になっていません。

戻る