【センリのラボノート②】ごはんWordleで遊ぼう！【PythonでTwitterを自動化したよ】

# 　ステップ１「はじめに」

　センリの道も一歩から！
　みんなおはよう～、水彩センリだよ

　今日はラボノートの新しいページを紹介するね
　前回の記事を読んでいない人にも分かるように書くけど、前回説明した部分は説明省略しちゃうよ～

https://qiita.com/SuisaiSenri/items/a958363863b7436bc840

　今回の記事は
　　ステップ１「はじめに」…………開発環境や概略
　　ステップ２「Are You Ready？」…………ディレクトリの準備
　　ステップ３「コードを書いていこうねぇ」…………スクリプト
　　ステップ４「細かく細かく砕こう！」…………詳細説明
　　ステップ５「これからもよろしくね」…………改善点の洗い出し

　っていう進行をするから、順に見ていってね！！

### 　第二回はこれ！
　今日紹介するのは、現在テスト中のあれ！

　「センリのごはんWordle」だよ～

https://twitter.com/SuisaiSenri/status/1508356970603945984?s=20&t=8Cd_oXWd3-b7HYWXp7hpRg

https://twitter.com/SuisaiSenri/status/1508360747063029760?s=20&t=8Cd_oXWd3-b7HYWXp7hpRg

https://twitter.com/SuisaiSenri/status/1508360745985011712?s=20&t=8Cd_oXWd3-b7HYWXp7hpRg

:::note
　画像提供に協力してくれたのは[スッチーさん](https://twitter.com/Lotte_Respect)
　柔らかなイラストが心落ち着く配信者さんだよ！

　ゲーム配信の他に、お絵描きライブをやってるんだって！
　4/24～4/30に開催されるVtuberクリエイトフェスティバルに出展すべく、絶賛イラスト作成中！

　完成が今から楽しみだねぇ。皆も一緒に応援しようねっ！
　→(https://www.youtube.com/channel/UCfZz3WBvmmSNwTPM1YJywQQ)
:::

### 　仕様
　１、正解を作成する
　２、解答を貰う
　３、何回目の解答かチェックする
　４、解答と正解を比較して⬜🟨🟩で返信
　５、正解が出たら紹介する
　６、以降の解答は締め切る

### 　ラボの環境
　センリの開発環境を説明するよ
~~~
Windows 10 Home
Visual Studio Code
Python 3.10.1

tweepy 4.5.0
~~~
　tweepyのインストール、TwitterAPIの取得は各自で別途調べてね！

### 　参考リンク
　今回はどこも参考にしてないから、粗が目立つかもしれないの(∩´﹏`∩)

# 　ステップ２「Are You Ready？」
　たまには英語、使いたくなるよね！

　ファイルやディレクトリの準備をしていくよ～

### 　ごはんリストの作成
　普段食べたものを思いつく限りリストに記入するよ～

　特に読まなくても良いけど、興味がある人は開いてね

<details><summary>　ごはんリスト（折りたたみ）</summary>

~~~wordList.txt
あらじる
いかめし
うにどん
おにぎり
おむれつ
おさしみ
せきはん
かきあげ
かけそば
かけもち
かつどん
かにたま
きんとん
くずもち
クッキー
くれーぷ
ここっと
さいだー
ざるそば
しぇいく
しめさば
すいとん
すきやき
ぜんざい
ぞうすい
そうめん
たいやき
かこやき
たんめん
とんかつ
にらたま
ぱえりあ
ばってら
ばばろあ
びーふん
ぴざまん
びびんば
ぴろしき
ぼるしち
みつまめ
らーめん
りぞっと
いそじる
いももち
まきずし
きもすい
よせなべ
くしかつ
さけめし
にざかな
はるまき
やきなす
やきとり
やきにく
むしぱん
むしどり
みずたき
てんぷら
ゆどうふ
とんじる
にくそば
にくまん
かすじる
あげそば
ひやじる
あじふらい
あなごどん
いかふらい
いくらどん
いなりずし
うぃんなー
うなぎどん
うなじゅう
えびぴらふ
えびふらい
おみそしる
おむらいす
おちゃづけ
かきふらい
かけうどん
かつかれー
かつさんど
かれーぱん
かれーまん
きつねそば
きむちなべ
ごまだんご
こんぽーと
さつまじる
さらだまき
ざるうどん
しゅーまい
そぼろどん
たぬきそば
ちきんかつ
ちゃーはん
つなさらだ
てぃらみす
ところてん
とろろそば
なぽりたん
にぎりずし
にしんそば
にゅうめん
はむえっぐ
はんばーぐ
びーふかつ
まぐろかつ
まぐろどん
みぞれじる
めんちかつ
もろきゅう
らざーにあ
わらびもち
いそべもち
ぎゅうどん
くりごはん
つきみそば
ごこくまい
ごもくずし
ごもくまめ
ちらしずし
わかたけに
てまきずし
やきうどん
おやこどん
すれんこん
せんばじる
たにんどん
ちくぜんに
てっかまき
まめごはん
ぶたきむち
にくうどん
にくじゃが
こふきいも
めだまやき
たまごやき
たまごやき
たまごどん
さといもに
たつたあげ
ひややっこ
いりどうふ
いりたまご
あさりごはん
あっぷるぱい
いかのにつけ
いんどかれー
えびてんどん
はやしらいす
きつねうどん
てんぷらそば
おこのみやき
たまごすーぷ
かきあげそば
かきあげどん
かつばーがー
かにぐらたん
かにたまどん
かるぱっちょ
かれーうどん
かれーすーぷ
かれーらいす
きつねうどん
きなこごはん
きのこごはん
きゅうりまき
けんちんじる
こーるすろー
こーんぴらふ
ごぼうさらだ
ころっけぱん
ささみふらい
しゃぶしゃぶ
すなっくがし
すぱげってぃ
そばがきじる
たぬきうどん
たんたんめん
ちーずけーき
ちーずけーき
ちーずさらだ
ちきんかれー
ちきんそてー
ちきんどりあ
ちきんぴらふ
ちきんらいす
どらいかれー
とろみすーぷ
とろろうどん
のっぺいじる
ばたーらいす
はむすてーき
はやしらいす
はんばーがー
ばんばんじー
ぴざとーすと
ぶりだいこん
ぽーくそてー
ほっとけーき
ほっとどっぐ
ぽてとさらだ
まーぼーなす
まーぼーどん
みーとぼーる
みーとろーふ
みそらーめん
みっくすぴざ
みもざさらだ
みるくせーき
みるふぃーゆ
めれんげぱい
もやしいため
もんじゃやき
ゆかりごはん
ゆばちゃづけ
ろーるけーき
わかめうどん
わかめすーぷ
わんたんめん
うのはなじる
しおらーめん
つきみうどん
さばのみそに
さんぺいじる
やまかけどん
さんさいそば
にこみうどん
わかたけじる
やきおにぎり
やきぎょうざ
やきそばぱん
やきそばぱん
あかだしじる
だいがくいも
ちゃわんむし
ちゅうかそば
ちゅうかどん
てんぷらそば
まめのさらだ
とうふさらだ
とうふさらだ
なんばんやき
なっとうまき
なっとうまき
はっぽうさい
やさいかれー
やさいさらだ
やさいぜりー
やさいそてー
あげたこやき
たまごどうふ
ちからうどん
わふうさらだ
あんにんどうふ
あんもちぞうに
だしまきたまご
えびかつさんど
ぐらたんすーぷ
おにおんすーぷ
おむれつけーき
かきあげうどん
かつおのたたき
かぼちゃぷりん
きゃらめるらて
きんぴらごぼう
こーひーぜりー
こんそめすーぷ
サーモンマリネ
さんどうぃっち
しーざーさらだ
しょーとけーき
すいーとぽてと
すこっちえっぐ
すてぃっくぱい
たけのこごはん
だしまきたまご
たたききゅうり
だぶるばーがー
ちーずばーがー
ちきんぐらたん
ちきんばーがー
ちゃんぽんめん
はにーとーすと
にしょくぜりー
ぱうんどけーき
ばけっとさんど
びーふしちゅー
びーふすてーき
ふかひれすーぷ
ふらんくふると
ふるーつさらだ
ふるーつさんど
ふるーつぜりー
ふるーつぽんち
まーぼーどうふ
まかろにさらだ
まんごーぷりん
みっくすさんど
みねすとろーね
らいすばーがー
ろーすとちきん
ろーすとびーふ
ろーるきゃべつ
わんたんすーぷ
おんせんたまご
かいそうさらだ
かまあげうどん
とりのからあげ
とりのとまとに
とりのてりやき
こうはくなます
さけのむにえる
さばのみそしる
さんしょくどん
さんさいうどん
さんさいおこわ
さんしょうやき
はるさめさらだ
たきこみごはん
あおなのそてー
だいこんさらだ
ちゃきんしぼり
ちゅうかちまき
てんぷらうどん
とうにゅうなべ
とうふぐらたん
とうふすてーき
にくだんごなべ
やさいころっけ
あげだしどうふ
たまごぞうすい
ひやしちゅうか
ぶりのてりやき
いきなりだんご
あさりのみそしる
あさりのさかむし
あすぱらべーこん
いかしょうゆやき
うぃんなーろーる
うなぎのかばやき
かすたーどぷりん
かぼちゃころっけ
かぼちゃのにもの
きのこのみそしる
くりーむしちゅー
こーんぽたーじゅ
さざえのつぼやき
しーふーどさらだ
じゃーまんぽてと
すらいすおにおん
ちゃーしゅーめん
クラムチャウダー
てりやきばーがー
ふれんちとーすと
とろろこんぶじる
とんこつらーめん
びーんずしちゅー
ぷりんあらもーど
ふるーつばばろあ
ふるーつみっくす
ふれんちとーすと
ふろふきだいこん
まーぼーはるさめ
まかろにぐらたん
みっくすじゅーす
よーぐるとさらだ
りんごのふらんべ
れあちーずけーき
くりのしぶかわに
さけのほいるやき
さしみこんにゃく
さんまのしおやき
にんじんぐらっせ
あおなのおひたし
あおなのごまあえ
あおなのしらあえ
きりぼしだいこん
だいこんのにもの
とりなんばんそば
とうふのあんかけ
とうふのでんがく
とうふはんばーぐ
ぶたにくのかくに
にくのやさいまき
にくやさいいため
まっちゃきんとき
あじつけかずのこ
やさいのごまあえ
やさいののりまき
やさいのてんぷら
やさいろーるかつ
さといもでんがく
れいしゃぶさらだ
わふうはんばーぐ
うぃんなーこーひー
がーりっくとーすと
かぼちゃぽたーじゅ
きのこすぱげってぃ
きのこのいためもの
きゅうりのすのもの
ごーやちゃんぷるー
こんにゃくいために
さつまいもばたーに
すくらんぶるえっぐ
すぱげってぃさらだ
たぴおかここなっつ
たらこすぱげってぃ
ちょこれーとけーき
ぴーまんのにくずめ
ひじきのいためもの
ふぃっしゅばーがー
ふるーつよーぐると
ぎゅうにくのたたき
さんしょくおにぎり
だいこんのみそしる
ちゅうかふうすーぷ
とうにゅうみそしる
とうふのすましじる
とうふのつくねあげ
はくさいのおひたし
やさいのたまごとじ
~~~

</details>

### 　ディレクトリ
　それじゃあ、ディレクトリがこんな感じになるよう作成＆配置してね

~~~
/Senri
├── reply-list.txt　　（既返信リスト）
├── done-log.txt　    （実行ログ）
├── auth.py　　　　　  （APIファイル）
├── Functions.py	  （関数ファイル）
├── run.py　　　　　　 （実行ファイル）
├── run.bat           （バッチファイル）
├── Automation.py　　　（定期ツイートファイル）
├── Automation.bat　　 （定期ツイートバッチファイル）
└── /wordle
	├── wordList.txt  （ごはんリスト）
	├── able.txt　　　 （可返信リスト）
	├── answer.txt　　（正解ファイル）
	├── question.txt　（出題ファイル）
	└── /reactions　　（解答フォルダ）

~~~

# 　ステップ３「コードを書いていこうねぇ」
　センリはAPIファイルと関数ファイル、実行ファイルの三つに分けて動かしてるよ

　APIは大事に保管したいし、実行ファイルは簡潔にまとめたいからね～

:::note warn
　説明用に用語を定義してるよ

【リプライ】
　センリが受け取るもの

【返信】
　センリが送るもの

【可返信リスト】
　ここにリプライを貰った場合、返信するよ～っていうツイートたち
　→ただのリプライと、解答リプライを区別するために必要だね

【既返信リスト】
　センリが返信し終わったツイートたち
　→何度も返信しないようにリスト化してるよ
:::

### 　APIファイル
　ここにTwitterAPIを入力するよ
~~~auth.py
consumer_key="xxxxxxxxxxxxxxxxxxxxxxxxxx"
consumer_secret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
access_token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
access_token_secret="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

~~~

### 　定期実行ファイル
　一日一回、出題するために使うよ～
~~~Automation.py
from Functions import *

if __name__ == '__main__':
    refresh()

~~~

### 　実行ファイル
　解答のチェックをするよ
　センリは5分間隔で動かすようにしてるけど、お好みで良いと思うんだぁ
~~~Run.py
from Functions import *

if __name__ == '__main__':
    ReplytoReply()

~~~

### 　関数ファイル
　中身はここに書き込むね
　関数部分と実行部分を分けると、機能追加が楽なんだよ～

　前回実装した関数も一部あるけど、ちゃんと全部書くね～

:::note warn
　すっごく長いから折りたたんでるよ！　読むときは開いてね！！
:::

<details><summary><font color="#ff4500"><b>　関数ファイル、折りたたみ</b></font></summary>

~~~Functions.py
import os
import shutil
import sys
import time
import random
import re
from logging import getLogger, StreamHandler, FileHandler, DEBUG, Formatter

#今回のTwitterモジュール
import tweepy

#auth.pyに書いてるキーをimportします
from auth import(
    consumer_key,
    consumer_secret,
    access_token,
    access_token_secret)

#Tweepyのおまじないです
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)

#ログ出力のおまじない
logger = getLogger(__name__)
handler1 = StreamHandler()
handler1.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s"))
handler2 = FileHandler("done-log.txt", encoding="utf-8-sig")
handler2.setLevel(DEBUG)
handler2.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s"))
logger.setLevel(DEBUG)
logger.addHandler(handler1)
logger.addHandler(handler2)
logger.propagate = False

#君の名は。
my_name = "SuisaiSenri"


#可返信リスト/既返信リストの取得関数だよ
def get_listfile(file):
    #可返信リストのファイルを読み込みます
    with open(file, mode='r', encoding="utf-8-sig") as f:
        reader = f.readlines()
    f.close()

    #可返信ツイートのリストを作ります。改行文字ジャマなので消すね～
    return [x.rstrip('\n') for x in reader]


#返信して良いかどうかの確認
def able_check(tweet, ablelist, alreadylist):
    #貰ったリプライのリプライ先が可返信ツイートであることを確認します
    if tweet.in_reply_to_status_id_str in ablelist:
        usr = tweet.user.screen_name + ":" + tweet.id_str

        #未返信であることを確認します
        if usr not in alreadylist:
            return True
        else:
            return False
    else:
        return False


#可返信ツイートの記録
def save_Replyable(tweet, file):
    #可返信ツイートに追記します
    #aは末尾への追記モード、encodingは絵文字認識のためsig
    with open(file, mode='a', encoding="utf-8-sig") as f:     
        f.write("\n" + tweet.id_str)
    f.close()


#既返信リストの更新
def already(alreadylist):
    #ファイルを開き、書き出します。
    with open("reply-list.txt", mode='w', encoding="utf-8-sig") as f:
        f.write('\n'.join(alreadylist))
    f.close()


#貰ったリプライへの返信
def ReplytoReply():
    #可返信リスト、既返信リストを取得
    ablelist = get_listfile("wordle/able.txt")
    alreadylist = get_listfile("reply-list.txt")

    #貰ったリプライを取得します
    try:
        #こまめに動かす予定なので検索数は10件程度
        tweets = api.mentions_timeline(count=10, tweet_mode='extended')
        #古いツイートから順番に返信したいので、reversedを使います
        tweets = reversed(tweets)
    except tweepy.errors.TweepyException as e:
        logger.error(e.msg)
        sys.exit(1)

    for tweet in tweets:
        try:
            #いいね数が0ならいいねを付けます
            if tweet.favorite_count == 0:
                api.create_favorite(tweet.id)
        except tweepy.errors.TweepyException:
            logger.info(f"いいねに失敗しました\n@{tweet.user.screen_name}")
        #返信しても良いリプライか考えます。
        check = able_check(tweet, ablelist, alreadylist)
        if check == True:
            try:
                #リプライ操作
                wordle(tweet)
            except tweepy.errors.TweepyException:
                pass
            else:
                logger.info("リプライしました: \n{0}".format(tweet.full_text))
                #既返信リストを準備します
                usr = tweet.user.screen_name + ":" + tweet.id_str    
                alreadylist.append(usr)
                time.sleep(random.uniform(6,10))
    #既返信リストを更新します
    already(alreadylist)

#リプライ実行
def Reply(tweet, sentence):
    #名前の加工
    name = nameProcess(tweet.user.name)

    #名前が消滅した場合、IDで呼ぶ
    if name == "":
        name = tweet.user.screen_name
    
    #@ID を文頭に付けなければリプライとして処理されない
    name = "@" + tweet.user.screen_name + "\n" + name
    text = name + sentence
    #よろしくお願いしまぁぁぁすっ!!
    return api.update_status(status=text, in_reply_to_status_id=tweet.id)



#名前の加工部分
def nameProcess(name):
    name = name.rstrip('\n')
    r = r"[\(（\[<\{＜「『“【［〈｛《〔‘].*?$"
    if re.sub(r, "", name) == "":
        r = r"\(.+?\)|（.+?）|\[.+?\]|<.+?>|\{.+?\}|＜.+?＞|「.+?」|『.+?』|“.+?”|【.+?】|［.+?］|〈.+?〉|｛.+?｝|《.+?》|〔.+?〕|‘.+?’"
    p = re.sub(r, "", name)
    r = r"[@＠].*?$"
    if re.sub(r, "", p) == "":
        r = r"[@＠]"
    p = re.sub(r, "", p)
    r = r"[/／\|｜:：].+?$"
    if re.sub(r, "", p) == "":
        r = r"[/／\|｜:：]"
    p = re.sub(r, "", p)
    r = r"[\u0000-\u007F\uFF01-\uFF0F\uFF1A-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\u3000-\u303F\u3041-\u309F\u30A1-\u30FF\uFF66-\uFF9F\u2E80-\u2FDF\u3005-\u3007\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\U00020000-\U0002EBEF]+"
    m = re.search(r, p)
    if m:
        p = m.group()
    else:
        p = ""
    r = r"[!！]+"
    p = re.sub(r, "", p)
    r = r"bot$|Bot$|BOT$"
    p = re.sub(r, "", p)

    #それでも文字数が過剰な場合、空白以降も消します
    if len(p) > 18:
        r = r"[ 　].+?$"
        p = re.sub(r, "", p)

    #名前が消滅している場合もあるので場合分け
    if p != "":
        #名前に最初から敬称が付いてる人はそのまま呼んであげます。他はさん付け
        r = r"たん$|さん$|ちゃん$|くん$|君$|様$"
        if re.search(r, p):
            title = ""
        else:
            title = "さん"    
        p = p + title
    return p


#毎日の出題を行います
class refresh():
    def __init__(self):
        #ごはんリストを取得します
        self.wordList = get_listfile("wordle/wordList.txt")
        self.refresh()

    def refresh(self):
        with open("wordle/able.txt", mode='w', encoding="utf-8") as f:     
            f.write("")
        f.close()

        answer = random.choice(self.wordList)

        #正解を正解ファイルに書き込みます
        with open("wordle/answer.txt", mode="w", encoding="utf-8")as f:
            f.write(answer)
        f.close()

        self.Tweet(len(answer))

        #リアクションファイルのリセット
        target_dir = 'wordle/reactions'
        shutil.rmtree(target_dir)
        os.mkdir(target_dir)

    def Tweet(self, n):
        try:
            text = f"センリのごはんwordle～！\n\nQ、センリは今日何を食べたでしょう？\n\nルール：ひらがな{n}文字のごはんをリプライしてね\n回答は一人6回までだよ～"
            #ツイート実行
            tweet = api.update_status(status=text)
        #送信できない場合の処理
        except tweepy.errors.TweepyException:
            logger.info("送信に失敗しました")
        else:
            save_Replyable(tweet, "wordle/able.txt")

            #出題ツイートのIDを記録します
            with open("wordle/question.txt", mode='w', encoding="utf-8") as f:     
                f.write(tweet.id_str)
            f.close()


class wordle():
    def __init__(self, tweet):
        with open("wordle/answer.txt", mode="r", encoding="utf-8")as f:
            answer = f.read()
        f.close()
        
        #正解が出た後か調べます。正解が出た時にanswerは""になる仕組みだよ
        if answer == "":
            self.correct = True
        else:
            self.correct = False

        self.num = len(answer)
        self.answer = answer
        self.tweet = tweet

        self.usr = self.tweet.user.screen_name + ":" + self.tweet.id_str
        self.reply = self.tweet.full_text[len(my_name)+2:]
        while(len(self.reply) < self.num):
            self.reply = self.reply + " "

        self.wordle()

    def wordle(self):
        if self.correct == False:
            id = self.tweet.user.screen_name

            try:
                #解答ファイル
                with open(f"wordle/reactions/{id}.txt", mode="r", encoding="utf-8-sig")as f:
                    reactions = f.readlines()
                f.close()
            except:
                a = 1
            else:
                #解答数を調べる
                a = len(reactions)

            react = self.reaction()
            text = f"の{a}回目の回答～\n\nただいまの結果:{react}"

            if "🟨" not in react and "⬜" not in react and a <= 6:
                text = text + f"\n\nおめでとう!\n正解は{self.answer}でした～"

                name = nameProcess(self.tweet.user.name)
                #名前が消滅した場合、IDで呼ぶ
                if name == "":
                    name = id

                self.Tweet(name, id)

            elif a == 6:
                text = text + f"\n\n残念!\n今日の回答はここまでだよ～\n\nまた明日も挑戦してねっ！"
            
            elif a > 6:
                text = f"\n回答は6回までだよ～\n\nまた明日も挑戦してねっ！"

            with open(f"wordle/reactions/{id}.txt", mode="a", encoding="utf-8-sig")as f:
                f.write("\n" + react)
            f.close()

            address = Reply(self.tweet, text)
            save_Replyable(address, "wordle/able.txt")
        
        else:
            text = "今日はもう正解が出ちゃったんだあ\n\nまた明日も挑戦してねっ！"
            Reply(self.tweet, text)
        save_Replyable(self.tweet, "wordle/able.txt")

    def reaction(self):
        answer = list(self.answer)
        a = []

        for n in range(self.num):
            if self.reply[n] == answer[n]:
                    a.append("2")
                    answer[n] = "a"
            else:
                a.append("0")
        
        for n in range(self.num):
            if a[n] == "0":
                if self.reply[n] in answer:
                    pos = answer.index(self.reply[n])
                    a[n] = "1"
                    answer[pos] = "a"

        react = "".join(a)

        react = react.replace("2", "🟩")
        react = react.replace("1", "🟨")
        react = react.replace("0", "⬜")

        return react

    def Tweet(self, name, id):
        try:
            with open("wordle/answer.txt", mode="r", encoding="utf-8")as f:
                answer = f.read()
            f.close()

            with open("wordle/question.txt", mode="r", encoding="utf-8")as f:
                q = f.read()
            f.close()

            url = f"https://twitter.com/{my_name}/status/{q}"

            text = f"今日の正解者が出たよ～！\n答えは{answer}でした～\n\n一番乗りは{name}（@{id}）\nおめでとう～！\nみんな明日もぜひぜひ挑戦してねっ！\n{url}"
            #ツイート実行
            api.update_status(status=text)

            with open("wordle/answer.txt", mode='w', encoding="utf-8") as f:     
                f.write("")
            f.close()
        #送信できない場合の処理
        except tweepy.errors.TweepyException as e:
            logger.error(e.msg)




~~~

</details>

# 　ステップ４「細かく細かく砕こう！」
　今回もしっかり説明していくよ～


~~~able_check().
def able_check(tweet, ablelist, alreadylist):
    if tweet.in_reply_to_status_id_str in ablelist:
        usr = tweet.user.screen_name + ":" + tweet.id_str

        if usr not in alreadylist:
            return True
        else:
            return False
    else:
        return False

~~~
　これは返信して良いのか確かめる関数だよ。貰ったリプライが

　・送信先が「可返信リスト」に含まれている
　・既返信リストに含まれていない

　っていう二点をクリアしてたら返信するんだぁ


~~~ReplytoReply().
    try:
        tweets = api.mentions_timeline(count=10, tweet_mode='extended')
        tweets = reversed(tweets)

~~~
　ツイートの取得は最新のものから行われるよ
　時系列順に返信しないと、先に正解者が出た時の対応を間違えちゃうから注意だね～


~~~ReplytoReply().
        try:
            if tweet.favorite_count == 0:
                api.create_favorite(tweet.id)

~~~
　検索するたびにいいねを押しちゃうから、いいねの数が0のときだけにしたよ

　センリが知らないだけで、既にいいねしたかどうかの値が存在するかも……？
　返信して良いツイートじゃない場合でもいいねは付けておきたいから、このタイミングでいいね操作したいの(ﾉω·｀o)

~~~refresh().refresh()
        answer = random.choice(self.wordList)

        with open("wordle/answer.txt", mode="w", encoding="utf-8")as f:
            f.write(answer)
        f.close()

~~~
　答えをランダムで選択、ファイルに書き込んでおくよ

~~~refresh().refresh()
        target_dir = 'wordle/reactions'

        shutil.rmtree(target_dir)
        os.mkdir(target_dir)

~~~
　解答フォルダを空にしておくよ
　一旦フォルダを削除して、同名のフォルダを作成する感じ～


~~~refresh().Tweet()
            with open("wordle/question.txt", mode='w', encoding="utf-8") as f:     
                f.write(tweet.id_str)
            f.close()

~~~
　解答発表に使うから、出題ツイートを別ファイルに保存しておくよ


　ここからはお待ちかね、Wordle機能の本体だよ
~~~wordle().__init__().
        with open("wordle/answer.txt", mode="r", encoding="utf-8")as f:
            answer = f.read()
        f.close()
        
        if answer == "":
            self.correct = True
        else:
            self.correct = False

~~~
　解答ファイルが空白かどうかで挙動が変わるようにしてるよ

　これはね、正解者が出た時に解答ファイルを空白に書き換えてるからなんだ～

~~~wordle().__init__().
        self.reply = self.tweet.full_text[len(my_name)+2:]
        while(len(self.reply) < self.num):
            self.reply = self.reply + " "

~~~
　Twitterのテキストは「@SuisaiSenri （ツイート本文）」っていう形だから、@と" "の2文字を除いたところからが本文なんだ～

　もしも6文字で答えてねって言ってるのに解答が5文字しかなかった場合！
　このときは空白で文字数を補ってあげるよ～

~~~wordle().wordle()
            try:
                with open(f"wordle/reactions/{id}.txt", mode="r", encoding="utf-8-sig")as f:
                    reactions = f.readlines()
                f.close()
            except:
                a = 1
            else:
                a = len(reactions)

~~~
　まだ解答したことのない人は解答ファイルが存在しないから、エラーになっちゃうの～
　そのときは1回目の解答ってことになるね

　既に解答したことのある人は、解答ファイルを参照するよ
　一行目が空白*1 だからそのまま要素の数を見たら解答数になるんだね

　*1……あとで　f.write("\n" + react)　っていう書き方をするんだけど、一行目に空白が入って不格好なの
　でもそのおかげで要素の数が解答数そのままになって楽だから、これで良いのかなぁ

~~~wordle().wordle()

            react = self.reaction()
            text = f"の{a}回目の回答～\n\nただいまの結果:{react}"

            if "🟨" not in react and "⬜" not in react and a <= 6:
                text = text + f"\n\nおめでとう!\n正解は{self.answer}でした～"

                name = nameProcess(self.tweet.user.name)
                if name == "":
                    name = id

                self.Tweet(name, id)

            elif a == 6:
                text = text + f"\n\n残念!\n今日の回答はここまでだよ～\n\nまた明日も挑戦してねっ！"
            
            elif a > 6:
                text = f"\n回答は6回までだよ～\n\nまた明日も挑戦してねっ！"

~~~
　解答が緑一色で、解答数が6回以下なら正解！
　解答数が6回で不正解なら、今日は解答上限のお知らせ
　解答が7回目以降なら締め切りの返信をするよ

~~~wordle().reaction()
        answer = list(self.answer)
        a = []

        for n in range(self.num):
            if self.reply[n] == answer[n]:
                    a.append("2")
                    answer[n] = "a"
            else:
                a.append("0")
        
        for n in range(self.num):
            if a[n] == "0":
                if self.reply[n] in answer:
                    pos = answer.index(self.reply[n])
                    a[n] = "1"
                    answer[pos] = "a"

        react = "".join(a)

~~~
【問題点】
　ここはたくさん考えたよ～
　🟩の条件は完全一致だから良いけど、🟨が難しかったぁ(∩´﹏`∩)

　「完全一致じゃないけど検索で引っかかる」のを🟨にするとね、
　正解が「にしんそば」のとき、「ちらしずし」って答えると「⬜⬜🟨⬜🟨」になっちゃうの

　本家のルールだと「⬜⬜🟨⬜⬜」になるはずだから、そこを実現するために工夫したよ

【解決法】
　まず一回完全一致だけ探して、⬜🟩だけの状態にするよ。
　そのとき正解の文字列から該当の文字を削除、"a"に置き換えておくよ

　同様に🟨の調査をしたとき、その正解の中で引っかかった文字を"a"に置き換えるの！

　こうすることで【問題点】の部分が解決したんだぁ

~~~wordle().Tweet()
            with open("wordle/answer.txt", mode='w', encoding="utf-8") as f:     
                f.write("")
            f.close()

~~~
　正解が出たから、正解ファイルを空にしておくよ～

## 　自動実行
　出来上がったら、Run.py、Automation.pyを自動で実行するバッチファイルを準備するよ。
　タスクスケジューラで実行時間を決めたら完成～

　センリはAutomation.pyを毎日13時に、run.batを常時（5分おき）動かしてるよ

　ただ実行するだけでも良いんだけど、ウィンドウが毎回出てくるのはイヤだから最小化状態で実行してもらうね
~~~Run.bat
@echo off
cd /d %~dp0

:最小化状態で実行する
if not "%X_MIMIMIZED%"=="1" (
    set X_MIMIMIZED=1
    start /min cmd /c,"%~0" %*
    exit
)

cd C:\Users\...\Senri
python run.py

exit /b
~~~
　下の方のrun.pyをAutomation.pyにしたファイルも準備してね


# 　ステップ５「これからもよろしくね」
　まだまだ改善点があるから精査したいねっ！

### 　その１　wordleのロジック
　wordleの解答チェックで、for文を二回使ってるけど、一回でする方法ないかなぁ？

~~~wordle().reaction()
        answer = list(self.answer)
        a = []

        for n in range(self.num):
            if self.reply[n] == answer[n]:
                    a.append("2")
                    answer[n] = "a"
            else:
                a.append("0")
        
        for n in range(self.num):
            if a[n] == "0":
                if self.reply[n] in answer:
                    pos = answer.index(self.reply[n])
                    a[n] = "1"
                    answer[pos] = "a"

        react = "".join(a)

~~~

### 　その２　リストが少ない！
　ごはんリストが少ないかなぁ

　もっと未知の料理も増やしていきたいなっ！

### 　その３　ひらがなにしか対応してない！
　カタカナで答えちゃう人もいるから、対応できるようにしたいなぁ
　元のファイルにカタカナも記入しておくか、カタカナ→ひらがな変換のコードを混ぜるかって感じかな？

### 　その４ みんなの声
　コードの間違い、改善案があったらコメントで指摘してくれると成長に繋がって嬉しいんだぁ
　逐一修正していきたいから、どしどし指摘してね！

## 　次回予告
　大変っ！　ラボノートが無くなっちゃったんだぁ

　たくさん書いてたのに、どこで落としたんだろう……？
　でもでも大丈夫、ラボの皆にも手伝ってもらってすぐに探し出すよ！