マルコフ連鎖を使って文章を自動的にツイートする6つのステップ

以下の記事を参考に、マルコフ連鎖で自分らしい文章をツイートさせてみます。プログラムは某サーバーに置いてあるので、cronで自動的にツイートするよう仕込みますʅ(◔౪◔ ) ʃ

- [マルコフ連鎖を使って自分らしい文章を生成する(Python3)](https://qiita.com/tktcorporation/items/19d60e2859aff3e2020b#_reference-feda77a43ad8dcb75f87)
- [マルコフ連鎖を使って自分らしい文章をツイートする](https://qiita.com/hitsumabushi845/items/647f8bbe8d399f76825c)

今回作成したプログラムは、以下で入手することが可能です。ツイート履歴のcsvファイル、Twitter API Keyはセキュリティの観点から、.gitignoreで除外しています。
https://github.com/metal-chaos/markov-chain-twitter

# 環境
Python3での動作が前提です。

```
$ python3 -V
Python 3.7.0
```

パッケージがない場合は、適宜インストールする必要があります。今回、インストール方法については省きます。

# 目次：6つのステップ
1. 自分の全ツイートをダウンロードする
- マルコフ連鎖データベースの作成
- データベースへ保存
- TwitterのAPIキーを取得・入力
- ツイートしてみよう
- cronで自動化する

# ステップ①：自分の全ツイートをダウンロードする
まずは、自分の全ツイートをダウンロードしました。ツイートは「[自分の全ツイート履歴をダウンロードする](https://www.howtonote.jp/twitter/helpful/index1.html)」を参考に、tweets.csvをダウンロードします。

# ステップ②：マルコフ連鎖データベースの作成
tweets.csvを元に、マルコフ連鎖データベースを作成します。ここら辺の手順は、[マルコフ連鎖を使って自分らしい文章を生成する(Python3)](https://qiita.com/tktcorporation/items/19d60e2859aff3e2020b#_reference-feda77a43ad8dcb75f87)と同様です。

```processed_texts.py
import pandas as pd
import re

csvfilepath = input('csv filepath : ')
df = pd.read_csv(csvfilepath)

tweets = df['text']

replypattern = '@[\w]+'
urlpattern = 'https?://[\w/:%#\$&\?\(\)~\.=\+\-]+'
moviepattern = '\[動画\]'
picturepattern = '\[写真\]'
stickerpattern = '\[スタンプ\]'
notepattern = '\[ノート\]'

processedtweets = []

for tweet in tweets:
    i = re.sub(replypattern, '', tweet)
    i = re.sub(urlpattern, '', i)
    i = re.sub(moviepattern, '', i)
    i = re.sub(picturepattern, '', i)
    i = re.sub(stickerpattern, '', i)
    i = re.sub(notepattern, '', i)
    if isinstance(i, str) and not i.split():
        pass
    else:
        processedtweets.append(i)

processedtweetsDataFrame = pd.Series(processedtweets)
newDF = pd.DataFrame({'text': processedtweetsDataFrame})

newDF.to_csv('processed.csv')
```

```
$ python3 processed_texts.py
```

# ステップ③：データベースへ保存
文章からマルコフ連鎖のためのチェーン（連鎖）を作成して、データベースに保存します。

```schema.sql
drop table if exists chain_freqs;
create table chain_freqs (
    id integer primary key autoincrement not null,
    prefix1 text not null,
    prefix2 text not null,
    suffix text not null,
    freq integer not null
);
```

```storeTextsToDB.py
from PrepareChain import *
import pandas as pd
from tqdm import tqdm
import sys

def storeTextsToDB():

    df = pd.read_csv('./processed.csv')

    texts = df['text']

    print(len(texts))

    chain = PrepareChain(texts[0])
    triplet_freqs = chain.make_triplet_freqs()
    chain.save(triplet_freqs, True)

    for i in tqdm(texts[1:]):
        chain = PrepareChain(i)
        triplet_freqs = chain.make_triplet_freqs()
        chain.save(triplet_freqs, False)


if __name__ == '__main__':
    storeTextsToDB()
```

```
$ python3 storeTextsToDB.py
```

# ステップ④：TwitterのAPIキーを取得・入力
Twitterへのツイート用に、APIキーを取得・入力します。Twitter APIは未登録だったので、以下の記事を参考に登録、承認まで行い、APIキーを取得しました。

- [Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ](https://qiita.com/kngsym2018/items/2524d21455aac111cdee)

APIキーを取得できたら、`keys_sample.json`ファイルをコピーして`keys.json`ファイルを作成します。値の部分に、APIキーを入力しましょう。

```keys.json
{
    "consumer_key": "<consumer_key>",
    "consumer_secret": "<consumer_secret>",
    "access_token": "<access_token>",
    "access_token_secret": "<access_token_secret>"
}
```

これらの値は、`markovbot.py`で用いられるものです。

```markovbot.py
def create_oath_session(oath_key_dict):
    oath = OAuth1Session(
    oath_key_dict['consumer_key'],
    oath_key_dict['consumer_secret'],
    oath_key_dict['access_token'],
    oath_key_dict['access_token_secret']
    )
    return oath
```

# ステップ⑤：ツイートしてみよう
ここまで来たらツイートできるはずです。早速、実行してみましょう。


```markovbot.py
import json
from requests_oauthlib import OAuth1Session
from GenerateText import GenerateText
import random

def markovbot():
    keysfile = open('keys.json')
    keys = json.load(keysfile)
    oath = create_oath_session(keys)

    generator = GenerateText(random.randint(1,3))

    tweetmarkovstring(oath, generator)

def create_oath_session(oath_key_dict):
    oath = OAuth1Session(
    oath_key_dict['consumer_key'],
    oath_key_dict['consumer_secret'],
    oath_key_dict['access_token'],
    oath_key_dict['access_token_secret']
    )
    return oath

def tweetmarkovstring(oath, generator):
    url = 'https://api.twitter.com/1.1/statuses/update.json'
    markovstring = generator.generate()
    params = {'status': '[偽者です]' + markovstring}
    req = oath.post(url, params)

    if req.status_code == 200:
        print('tweet succeed!')
    else:
        print('tweet failed')


if __name__ == '__main__':
    markovbot()
```

```
$ python3 markovbot.py
```

なお、GenerateTextに渡す値で出力される文章量が変わるので、1〜3の値を渡す形にしています。ここら辺は実際に出力されるツイートの内容を見て、適宜調整すると良いでしょう。

```markovbot.py
generator = GenerateText(random.randint(1,3))
```

# ステップ⑥：cronで自動化する
cron（クーロン）を使って、指定の時間にツイートするように仕込んでみます。macOSやLinuxではクーロンというデーモンプロセスが用意されており、プログラムを指定の日時に自動で走らせることができます。早速、cronを開いてみます。

```
$ crontab -e
```

cronには、日時と走らせたい処理を記載できます。

```crontab
(分)(時)(日)(月)(曜日) 実行するコマンドのパス
```

今回はひとまず、平日の18時に起動させることにしました。

```crontab
00 18 * * 1-5 . $HOME/.bash_profile; cd <markovbot.pyが置いてあるディレクトリを記載>; python3 ./markovbot.py
```

なお、プログラムを走らせる前に、`.bash_profile`から環境変数を読み込んでいます。この部分はそれぞれの環境に依存するので、適宜カスタマイズすると良いでしょう。

```crontab
$HOME/.bash_profile;
```

また、`markovbot.py`が置いてあるディレクトリに移動させてからプログラムを走らせています。プログラム実行時にパスのエラーが出てしまうからです。

```crontab
cd <markovbot.pyが置いてあるディレクトリを記載>;
```

さぁ、ここまで来れば偽あなたが勝手にツイートしてくれるはずです。パチもんツイートライフを楽しみましょう🎅

> アイデンティティーの識別と付与を拒むという形のプライバシーを実践するには、メディア学者フィン・ブラントンと哲学者ヘレン・ニッセンバウムの言う「ぼかし」が最善の方法だ。つまり「曖昧で、紛らわしい、誤解を招く情報を意図的に加えることで、監視とデータ収集を妨げる」のである。 - [ジョン・チェニー＝リッポルド. WE ARE DATA　アルゴリズムが「私」を決める (Japanese Edition)](https://amzn.to/2J6Lh3Y) 

# 参考
- https://github.com/o-tomox/TextGenerator
- [マルコフ連鎖を使って自分らしい文章を生成する(Python3)](https://qiita.com/tktcorporation/items/19d60e2859aff3e2020b#_reference-feda77a43ad8dcb75f87)
- [マルコフ連鎖を使って自分らしい文章をツイートする](https://qiita.com/hitsumabushi845/items/647f8bbe8d399f76825c)