はじめに
新しくtwitter apiの無料プランが発表されましたので、botを作っていきたいと思います。
developer portalへの登録から定期投稿まで全部やります。
twitter api freeでは
- ツイートする (24時間に50回)
- (自分の)ツイート削除 (24時間に50回)
- (自分の)アカウントの情報手に入れる (24時間に25回)
ができます。(というか、これしかできない。ツイート取得さえできない...)
今回は、定期的にツイートをするbotをpythonで作ることを目指します。
追記(2024/08/05)
ここで紹介されている、無料で定期実行を行う方法は現在できなくなりました。
1月ごろの出来事です。
https://ask.replit.com/t/replit-24-7-and-uptimerobot/95014/4
https://uptimerobot.com/blog/replit-monitors-recent-issues/
などを見るとわかります。
developer portalへの登録
developer portalに行きます。下のSign up for Free Accountをクリックします。(画像のようにMissing Phone Numberと表示される場合はVerify Phoneをクリックして確認を行ってください。)
TwitterのデータとAPIの使用例を250文字以上で説明します。
私の場合こんな感じにしてみました。
I am currently learing programming with python. I am interested in creating a twitter bot using python and will use twitter api for the purpose of creating a simple bot that will tweet and delete tweets and their deletion. Machine translation is used. Sorry if there are any strange parts.
botを動かせるようにしていく
下の方にあるUser authentication settingsのSet upをクリックします。
App permissionsをRead and write,Type of AppをWeb App, Automated App or Botにします。
あとは、入力が必須なApp infoのCallback URI / Redirect URLとWebsite URLを入力します。どちらもhttps://twitter.com/
でokです。
Client IDとClient Secretがコピーできます。私の場合だと使わないのですが、一応コピーして安全な場所に保存しておくほうがいいかもです。ここまでできたら、Doneを押します。
Consumer KeysのAPI Key and SecretのRegenerateと、Authentication TokensのAccess Token and SecretのGenerateをクリックしてそれぞれコピーして保存しておきます。これは後で使用します。
これでDeveloper Portalの設定は以上です!お疲れさまでした!
プログラムを書く準備
今回はpythonを利用する環境としてhttps://replit.com/ を利用します。初めての方は右上のSign Upよりアカウントを作成してください。(それが完了したら下の画面に移行します)
secretを追加していきます。追加するのは
key | value |
---|---|
CONSUMER_KEY | API KEYの内容 |
CONSUMER_SECRET | API KEY SECRETの内容 |
ACCESS_TOKEN | Access Tokenの内容 |
ACCESS_TOKEN_SECRET | Access Token Secretの内容 |
この操作を行うのはセキュリティを考えてのことです。また、ここの「API KEYの内容」や「API KEY SECRETの内容」には、「botを動かせるようにしていく」で先ほど保存したコードを入力してくださいね。
これで、必要な準備は終わりです、実際にプログラムを書いていきましょう!
プログラムを書く
main.pyにプログラムを入力したら、「RUN」をクリックしてプログラムを実行します。
ツイートできるかテスト
import os
import tweepy
#secretsで設定した値をとる
CONSUMER_KEY = os.environ['CONSUMER_KEY']
CONSUMER_SECRET = os.environ['CONSUMER_SECRET']
ACCESS_TOKEN = os.environ['ACCESS_TOKEN']
ACCESS_SECRET = os.environ['ACCESS_SECRET']
#オブジェクト作成
client = tweepy.Client(
consumer_key = CONSUMER_KEY,
consumer_secret = CONSUMER_SECRET,
access_token = ACCESS_TOKEN,
access_token_secret = ACCESS_SECRET
)
#ツイートする
client.create_tweet(text='テスト')
https://twitter.com/home に行って確認すると、ちゃんとツイートされていることが分かりますね!
ツイートできることが確認できたら、あなたの目的に応じてプログラムを書きましょう。
メッセージを改行させたいときは、\n
をいれてください。
例えば'おはよう\nございます'だと
おはよう
ございます
のように表示されます。
同じ内容をツイートしようとすると、You are not allowed to create a Tweet with duplicate content.
(重複する内容のツイートを作成することは禁止されています。)というエラーがでてしまいます。このエラーがでたときはその内容はツイートできません。どこまでがこのエラーに引っかかるのか(何時間後でok、何個違うツイートを間にはさんだらokとか)はわかりませんが、定期投稿をさせるにあたっては、目的(行いたいプログラム)によってはこのエラーに気を付ける必要があります。
画像(動画)付きツイート
画像(動画)付きのツイートをさせたい場合
参考: https://zenn.dev/stucci/articles/195815871110c2
import os
import tweepy
#secretsで設定した値をとる
CONSUMER_KEY = os.environ['CONSUMER_KEY']
CONSUMER_SECRET = os.environ['CONSUMER_SECRET']
ACCESS_TOKEN = os.environ['ACCESS_TOKEN']
ACCESS_SECRET = os.environ['ACCESS_SECRET']
#認証
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
#オブジェクト作成
api = tweepy.API(auth)
client = tweepy.Client(
consumer_key = CONSUMER_KEY,
consumer_secret = CONSUMER_SECRET,
access_token = ACCESS_TOKEN,
access_token_secret = ACCESS_SECRET
)
image_path = "sample.png" #ファイルのパスをここに
#ツイート
media = api.media_upload(filename=image_path)
client.create_tweet(text="画像投稿テスト", media_ids=[media.media_id]);
その他
一応、ツイート以外のこともできるので紹介。
公式ドキュメント↓
使えるのは
- create_tweet() ツイート
- delete_tweet() ツイート削除
- get_me() 自分の情報取得
のみです。
Client.delete_tweet(ツイートのid)
でツイート削除。
Client.get_me()
で自分の情報が得られます。
実用性があるのはほぼcreate_tweet()だけだと思います。
一応引用ツイート(client.create_tweet(text="test2",quote_tweet_id=00000000);
)や、
フォローしてる人だけが返信できる(client.create_tweet(text="test2",reply_settings="following");
)
などもできました。だからどうしたって感じですが。
定期投稿
追記(2024/08/05)
上でも書きましたが、
ここで紹介されている、無料で定期実行を行う方法は現在できなくなりました。
1月ごろの出来事です。
https://ask.replit.com/t/replit-24-7-and-uptimerobot/95014/4
https://uptimerobot.com/blog/replit-monitors-recent-issues/
などを見るとわかります。
:::動かしたいプログラムが完成したら、定期投稿をできるようにします。
scheduleモジュールを使って定期的な実行を行います。ただし、replitはアクティビティがないとスリープしてしまいそもそもプログラムが動かなくなりますので、
uptimerobotを利用します。
uptimerobotは、ウェブサイトの監視サービスです。uptimerobotが監視をできるように、のちほどサーバを作っていきます。
:::note warn
1つ注意してほしいのは、この方法はあまり褒められたものではないということです。(replit側からすると無料でサーバーを使われ続けるということですから。)
有料ですが、replit公式が提供しているAlways Onを利用するのが本来であれば望ましいでしょう。
scheduleモジュールを使って定期実行
参考: https://di-acc2.com/programming/python/4574/
1時間に一回「テスト」と表示させるプログラムの例です。(実際は重複する内容はダメだよというエラーに引っかかりますよ)
import os
import tweepy
import schedule
from time import sleep
#secretsで設定した値をとる
CONSUMER_KEY = os.environ['CONSUMER_KEY']
CONSUMER_SECRET = os.environ['CONSUMER_SECRET']
ACCESS_TOKEN = os.environ['ACCESS_TOKEN']
ACCESS_SECRET = os.environ['ACCESS_SECRET']
#オブジェクト作成
client = tweepy.Client(
consumer_key = CONSUMER_KEY,
consumer_secret = CONSUMER_SECRET,
access_token = ACCESS_TOKEN,
access_token_secret = ACCESS_SECRET
)
#ツイートする
def tweet():
#定期実行したい部分のプログラムをここに入力します。
client.create_tweet(text='テスト')
#1時間に1回実行
schedule.every(1).hours.do(tweet)
while True:
schedule.run_pending()
sleep(1)
import schedule
とfrom time import sleep
を追加したら、定期実行したい部分のプログラムを関数化します。そして最後下の五行を追加します。
schedule.every(1).hours.do(tweet)
の部分を変えることで、いろいろできます。例えば30分に一回ならschedule.every(30).minutes.do(tweet)
。
詳しく説明するのは記事違いなので https://di-acc2.com/programming/python/4574/ を参考にお願いします。 freeプランでは24時間に50ツイートまでなので、頻度としては30分に1回程度が上限となります。
サーバの立ち上げ
次に、replitをスリープさせないためのuptimerobotが監視をできるようにサーバを立ち上げます。
まず、左のタブよりserver.py
を作成し以下のプログラムをコピペします。
from flask import Flask
from threading import Thread
app = Flask("")
@app.route("/")
def main():
return "bot is alive!"
def run():
app.run("0.0.0.0", port=8080)
def keep_alive():
t = Thread(target=run)
t.start()
次に、main.pyに戻りfrom server import keep_alive
と、keep_alive()
を追加します。
#省略
from server import keep_alive
keep_alive()
#省略
ここまでできたら、replitのRunをクリックします。以下のようにWebviewタブが開かれると思いますので、URLをコピーしておきましょう。
UptimeRobotの設定
https://uptimerobot.com/ に行きます。アカウントを作成したら、左上のAdd New Monitorをクリックします。
設定はMonitor Type
をHTTP(s)、Friendly Name
に好きな名前を入れたらURL (or IP)
に先ほどコピーしたURLを貼り付けます。
Monitoring Interval
をevery 20 minutesにしたら、下のほうにあるSelect "Alert Contacts To Notify" にチェックを入れ、Create Monitorをクリックし、完了したらClose。
これで完成です。replitを起動させ続けられるようになりました。お疲れさまでした!
実際には不安定であり、完全に365日24時間稼働とはいかないみたいです。(つまり、1時間に1回ツイートするようなプログラムであってもたまにかなり間があいてしまったりなどがありえます。)
さらに安定させたい場合はやはりpc常時起動や、Always Onへの課金などが必要みたいですね。
おわりに
記事を書くのは初めてなので、冗長な部分が多々あったと思います。もし何か間違いなどあればコメントで教えてください。
見てくださりありがとうございました。
おまけ
適当に思ったことを書きます。
順番ツイート
使い道がありそうな「順番にツイートしていく」プログラムを作ってみます(こういう単純なものだとわざわざプログラミングせずともtwittbot?みたいなのがあるのかもしれませんが)。順番にツイートするためには前何をツイートしたかという情報が必要ですが、完全に365日24時間ずっと稼働といえない状況では一度ダウンしたらまた1からということになりかねないので、それを防ぐためツイートするたびに何番目をツイートしたかをファイルに保存する方針で行きます。
次に、main.pyにこれをコピペ
import os
import tweepy
import schedule
from time import sleep
from server import keep_alive
keep_alive()
#secretsで設定した値をとる
CONSUMER_KEY = os.environ['CONSUMER_KEY']
CONSUMER_SECRET = os.environ['CONSUMER_SECRET']
ACCESS_TOKEN = os.environ['ACCESS_TOKEN']
ACCESS_SECRET = os.environ['ACCESS_SECRET']
#オブジェクト作成
client = tweepy.Client(
consumer_key = CONSUMER_KEY,
consumer_secret = CONSUMER_SECRET,
access_token = ACCESS_TOKEN,
access_token_secret = ACCESS_SECRET
)
tweetlist = [ #ツイートする内容をここに。
"ツイート内容1",
"ツイート内容2",
"ツイート内容3",
"ツイート内容4",
"ツイート内容5"
]
path = "number.txt"
n = 0
with open(path) as f:
n = f.read()
#ツイートする
def tweet():
#定期実行したい部分のプログラムをここに入力します。
global n
n = int(n)
client.create_tweet(text=tweetlist[n])
n += 1
if n >= len(tweetlist):
n = 0
with open(path, mode='w') as f:
f.write(str(n))
#3時間に1回実行
schedule.every(3).hours.do(tweet)
while True:
schedule.run_pending()
sleep(1)
〇時間に1回とか、ツイートする内容、その数などは自由に変えて使うことができます。ツイート内容重複エラーにはお気をつけて。
アナグラム
元ネタ: https://twitter.com/Fal_conpunch
import os
import tweepy
import schedule
import random
from time import sleep
from server import keep_alive
keep_alive()
#secretsで設定した値をとる
CONSUMER_KEY = os.environ['CONSUMER_KEY']
CONSUMER_SECRET = os.environ['CONSUMER_SECRET']
ACCESS_TOKEN = os.environ['ACCESS_TOKEN']
ACCESS_SECRET = os.environ['ACCESS_SECRET']
#オブジェクト作成
client = tweepy.Client(
consumer_key = CONSUMER_KEY,
consumer_secret = CONSUMER_SECRET,
access_token = ACCESS_TOKEN,
access_token_secret = ACCESS_SECRET
)
word = "ファルコン・パンチ" #ここにアナグラム化するワードを
#ツイートする
def tweet():
l = list(word)
random.shuffle(l)
result = ''.join(l)
client.create_tweet(text=result)
#1時間に1回実行
schedule.every(1).hours.do(tweet)
while True:
schedule.run_pending()
sleep(1)
蛇足
一応、この方法で私が作った「およそ1時間に一度、ランダムに色とその反転色を生成し、hex codeとrgbを表示するbot」がありますので、リンクとmain.pyを貼っておきます。
https://twitter.com/color_botTw
import os
import tweepy
import random
import schedule
import time
from PIL import Image, ImageDraw, ImageFont
from server import keep_alive
# ウェブサーバーを起動する
keep_alive()
#secretsで設定した値をとる
CONSUMER_KEY = os.environ['CONSUMER_KEY']
CONSUMER_SECRET = os.environ['CONSUMER_SECRET']
ACCESS_TOKEN = os.environ['ACCESS_TOKEN']
ACCESS_SECRET = os.environ['ACCESS_SECRET']
#認証
auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
#オブジェクト作成
api = tweepy.API(auth)
client = tweepy.Client(
consumer_key = CONSUMER_KEY,
consumer_secret = CONSUMER_SECRET,
access_token = ACCESS_TOKEN,
access_token_secret = ACCESS_SECRET
)
#ファイルパス指定
path = "colortype.txt"
#rgbをhexコードに
def rgb_to_hex(r, g, b):
return "#{:02x}{:02x}{:02x}".format(r, g, b)
#フォントの色を決める(黒か白)
def fontColor(r, g, b):
sum = r + g + b
if (sum>382):
return "#484848"
else:
return "#EBEBEB"
#決めたトーンの色を生成
def tone(color):
power = (random.randint(0,766)) / 765
color = color.strip("#")
rgb = list(int(color[i:i+2], 16) for i in (0, 2, 4))
R = int(rgb[0] * power)
G = int(rgb[1] * power)
B = int(rgb[2] * power)
return R, G, B
with open(path) as f:
colorType = f.read()
def change():
global colorType
n = random.randint(0, 10)
if(n <= 1):
colorType = "random"
message = "今日は完全ランダムに色を生成します。"
elif (n == 2):
colorType = "light"
message = "今日は明るい色中心に色を生成します。"
elif (n == 3):
colorType = "dark"
message = "今日は暗い色中心に色を生成します。"
elif (n == 4):
colorType = "red"
message = "今日は赤色中心に色を生成します。"
elif (n == 5):
colorType = "green"
message = "今日は緑色中心に色を生成します。"
elif (n == 6):
colorType = "blue"
message = "今日は青色中心に色を生成します。"
elif (n == 7):
colorType = "monochrome"
message = "今日はモノクロを生成します。"
elif (n == 8):
colorType = "sepia"
message = "今日はセピア色を生成します。"
else:
R = random.randint(0, 256)
G = random.randint(0, 256)
B = random.randint(0, 256)
colorType = rgb_to_hex(R, G, B)
message = "今日は"+rgb_to_hex(R, G, B)+"を基調とした色を生成します。"
client.create_tweet(text=message)
with open(path, mode='w') as f:
f.write(colorType)
#ツイート
def tweet():
if (colorType == "random"):
R = random.randint(0, 256)
G = random.randint(0, 256)
B = random.randint(0, 256)
elif (colorType == "light"):
R = random.randint(128, 256)
G = random.randint(128, 256)
B = random.randint(128, 256)
elif (colorType == "dark"):
R = random.randint(0, 128)
G = random.randint(0, 128)
B = random.randint(0, 128)
elif (colorType == "red"):
R = random.randint(128, 256)
G = random.randint(0, 128)
B = random.randint(0, 128)
elif (colorType == "green"):
R = random.randint(0, 128)
G = random.randint(128, 256)
B = random.randint(0, 128)
elif (colorType == "blue"):
R = random.randint(0, 128)
G = random.randint(0, 128)
B = random.randint(128, 256)
elif (colorType == "monochrome"):
R, G, B = tone("ffffff")
elif (colorType == "sepia"):
R, G, B = tone("f0c891")
else:
R, G, B = tone(colorType)
#反転色
R2 = abs(R-255)
G2 = abs(G-255)
B2 = abs(B-255)
#hexコードに変換する
hex = rgb_to_hex(R, G, B)
hex2 = rgb_to_hex(R2, G2, B2)
#無地の画像作成
img = Image.new("RGB", (1200, 800), (R, G, B))
img2 = Image.new("RGB", (300, 200), (R2, G2, B2))
draw = ImageDraw.Draw(img)
draw2 = ImageDraw.Draw(img2)
#フォントをいれる
font = ImageFont.truetype('arial.ttf', 150)
font2 = ImageFont.truetype('arial.ttf', 60)
draw.text((310, 305), hex, fontColor(R,G,B), font=font)
draw2.text((41, 65), hex2, fontColor(R2,G2,B2), font=font2)
#生成した画像を一つにする
img.paste(img2, (900,600))
image_path = "color.png"
img.save(image_path)
message = hex + "("+str(R)+", "+str(G)+", "+str(B)+\
")\n反転色は" + hex2 + "("+str(R2)+", "+str(G2)+", "+str(B2)+")"
media = api.media_upload(filename=image_path)
client.create_tweet(text=message, media_ids=[media.media_id]);
#00:00時点に毎日実行(utcとは9時間のズレ)
schedule.every().day.at("15:00").do(change)
#00分時点に毎時間実行
schedule.every().hour.at(":00").do(tweet)
while True:
schedule.run_pending()
time.sleep(1)