はじめに
こちらは東京高専AdventCalendar① 16日目の記事です。プログラミング初学者であるため、お見苦しい点が多々あると思いますが、何卒ご容赦いただければ幸いです。
さて、出端から私事を挟んで申し訳ありませんが、最近私のツイートに対するフォロワーさんの反応が鈍くなってきました。これも偏に私の努力不足が原因なのですが、このままでは遠からず精神的な破綻を迎えそうなので、火急の対症療法として自分で自分のツイートにリプライを送りつけることにしました。
とりあえずは、「助けてくれ」「もう疲れた」などの私がよく使用している負のワードを含むツイートに対して、慰めと労いと励ましの言葉をありがたい画像つきでリプライしてくれる、そんなハートフルなTwitterAPIの利用を目指しました。
使用要素
- 言語: Python3
- API: TwitterAPI
- モジュール: json, random, glob, time, requests_oauthlib
- 環境: Visual Studio 2019 (Pythonアプリケーション)
前提
- Python3の開発環境が整っていること (参照: Visual StudioでのPython チュートリアル)
- 上記のモジュールが全てインストールされており、正常に動作すること
- TwitterAPIの使用申請が受理され、APIキーを既に取得していること (参照: Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ)
- Twitterをやっていること
How To Make
1. APIキーの設定
まずは、定石として、main.py
とは別に.py
ファイルを新規作成して、そちらで変数にAPIキーを代入する工程を記述していきます(ここでは、config.py
というファイル名にしています)。
CONSUMER_KEY = '取得したConsumer API key'
CONSUMER_SECRET = '取得したConsumer API secret key'
ACCESS_TOKEN = '取得したAcccess token'
ACCESS_TOKEN_SECRET = '取得したAccess token secret'
これでAPIキーの設定は完了です。あとは、このconfig.py
をmain.py
にインポートして変数を利用するだけです。
2. main.py
の記述
main.py
の全体は以下の通りです。
#ファイル(config.py)とモジュールのインポート
import config
import json
import random, glob, time
from requests_oauthlib import OAuth1Session
#OAuth認証
CK = config.CONSUMER_KEY
CS = config.CONSUMER_SECRET
AT = config.ACCESS_TOKEN
ATS = config.ACCESS_TOKEN_SECRET
twitter = OAuth1Session(CK, CS, AT, ATS)
#画像添付リプライを実行する関数
def reply(replies, id):
url_media = 'https://upload.twitter.com/1.1/media/upload.json'
url_text = 'https://api.twitter.com/1.1/statuses/update.json'
images = glob.glob('images/*')
files = {'media': open(images[random.randrange(len(images))], 'rb')}
req_media = twitter.post(url_media, files = files)
media_id = json.loads(req_media.text)['media_id']
params = {'status': replies[random.randrange(len(replies))], 'media_ids': [media_id], 'in_reply_to_status_id': id}
req_text = twitter.post(url_text, params = params)
#3秒周期の繰り返し処理
while True:
#自分の最新ツイートを取得して、その中からキーワードのリスト(words)に該当する言葉が含まれているものだけを抽出する
url = 'https://api.twitter.com/1.1/statuses/user_timeline.json'
params ={'count': 1}
req = twitter.get(url, params = params)
words = ['助けてくれ', '辛い', 'キツい', 'ダメ', '無理','逃げたい', '嫌', '最悪', 'もう疲れた', '消えたい', '失敗した','線形代数落としました', '留年しました', '捕まりました']
#返信に使用されるワードのリスト(replies)に格納してある言葉をランダムに抽出してreply関数に渡す
replies = ['君はよく頑張ってるよ', 'きっと大丈夫だよ', '今日はたまたま調子が悪いだけさ', '明日は絶対に上手くいくよ', 'くよくよするより元気に行こう!', '何も心配することはないよ', '神は君をお許しになるでしょう', 'さすがだぞ!人生の苦難を経験しているんだな', '甘えるな', '情けない', '人間の面汚し']
if req.status_code == 200:
timeline = json.loads(req.text)
for word in words:
if word in timeline[0]['text']:
reply(replies, timeline[0]['id_str'])
print('Posted!')
else:
print('ERROR: %d' % req.status_code)
time.sleep(3)
次に、main.py
の簡潔な解説をします。
2.1. importとOAuth認証
#ファイル(config.py)とモジュールのインポート
import config
import json
import random, glob, time
from requests_oauthlib import OAuth1Session
#OAuth認証
CK = config.CONSUMER_KEY
CS = config.CONSUMER_SECRET
AT = config.ACCESS_TOKEN
ATS = config.ACCESS_TOKEN_SECRET
twitter = OAuth1Session(CK, CS, AT, ATS)
ここはほとんど一般的な雛形通りです。エンドポイントの読み込みに使用するjson
とOAuth認証を通すためのに使用するconfig.py
, requests_oauthlib
の他に、random
, glab
, time
をインポートして後述する処理に使用しています。
2.2. リプライを実行する関数
#画像添付リプライを実行する関数
def reply(replies, id):
url_media = 'https://upload.twitter.com/1.1/media/upload.json'
url_text = 'https://api.twitter.com/1.1/statuses/update.json'
images = glob.glob('images/*')
files = {'media': open(images[random.randrange(len(images))], 'rb')}
req_media = twitter.post(url_media, files = files)
media_id = json.loads(req_media.text)['media_id']
params = {'status': replies[random.randrange(len(replies))], 'media_ids': [media_id], 'in_reply_to_status_id': id}
req_text = twitter.post(url_text, params = params)
ここではコメントアウト文の通り、「画像添付リプライを実行する関数を定義」しております。画像をポストした後に、その画像の['media_id']
をmedia_ids
パラメータに格納したリプライをポストすると、画像が添付されたリプライを投稿することができます。
images
にはgrab.grab()
でローカルから取得した画像群の相対座標がリスト型で代入されており、その内1つをrandom.randrange()
を使用して無作為に選択して投稿する構造になっています。
正直、このローカルから画像を取得する機能は勢い余って実装したので、むしろ削ったほうが煩雑さがなくなって良いかなと案じております。
2.3. 最新ツイートの定期的な取得
#3秒周期の繰り返し処理
while True:
#自分の最新ツイートを取得して、その中からキーワードのリスト(words)に該当する言葉が含まれているものだけを抽出する
url = 'https://api.twitter.com/1.1/statuses/user_timeline.json'
params ={'count': 1}
req = twitter.get(url, params = params)
words = ['助けてくれ', '辛い', 'キツい', 'ダメ', '無理','逃げたい', '嫌', '最悪', 'もう疲れた', '消えたい', '失敗した','線形代数落としました', '留年しました', '捕まりました']
#返信に使用されるワードのリスト(replies)に格納してある言葉をランダムに抽出してreply関数に渡す
replies = ['君はよく頑張ってるよ', 'きっと大丈夫だよ', '今日はたまたま調子が悪いだけさ', '明日は絶対に上手くいくよ', 'くよくよするより元気に行こう!', '何も心配することはないよ', '神は君をお許しになるでしょう', 'さすがだぞ!人生の苦難を経験しているんだな', '甘えるな', '情けない', '人間の面汚し']
if req.status_code == 200:
timeline = json.loads(req.text)
for word in words:
if word in timeline[0]['text']:
reply(replies, timeline[0]['id_str'])
print('Posted!')
else:
print('ERROR: %d' % req.status_code)
time.sleep(3)
ここでは、url
に「自身のツイートを取得する」エンドポイントを代入して、パラメータを{'count': 1}
とすることで最新のものだけを取得しています。その後、あらかじめ用意しておいたキーワードのリストに該当するか非該当かの判定を行い、該当したら先述したreply()
関数に値を渡します。
また、while True
とtime.sleep()
によって、3秒周期で実行される仕組みになっています1。TwitterAPIにおけるhttps://api.twitter.com/1.1/statuses/user_timeline.json
の取得可能回数の上限は15分で900回 = 1秒に1回ですが、不安なので余裕をもたせました。
それと、やっぱりただ単にエールの言葉だけじゃつまらないので、スパイスとして軽い罵倒も混ぜました。
実行結果
無事に実行できました。これで傷は癒えるのでしょうか。
Posted!
結論
来世はちゃんとした人間からのリプライが欲しいです。
参照サイト
参考にさせていただいたQiita記事
公式リファレンス
-
正確に言えば
time.sleep()
の仕様上、厳密な3秒周期にはなっていませんが、このプログラムにおいては些細な誤差であると見做して無視することにします。詳細は、Pythonで定周期で実行する方法と検証を参照してください。 ↩