某動画サイト風にTwitter検索結果を流す【python】

某アイドルがniconicoでの配信をしていないので、先代アイドルに比べなんとなく生放送が寂しい…
→生放送の推奨ハッシュタグ付きツイートをオーバーレイすればいいのでは?

というわけでPythonでTwitterの検索結果を某動画サイト風に流すプログラムを作りました。

【追記】
[YouTubeのコメントver.](https://qiita.com/teahat/items/c44aca37223c54b0cf19)も作りました。

#イメージ図
![live.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240141/53d0da62-8b54-5060-8524-8987e7bab5f5.gif)

キャプチャは  https://www.youtube.com/watch?v=G4aogUF_M_Y  より。

#想定する利用例  
* 実況が盛んな生放送やAbema等での最速リアタイ視聴
* 任意のタグを指定したライトニングトーク等のフランクな発表会
* Twitterでコメを拾う配信者：[例@23:25~](https://youtu.be/qPWCBhJT-0g?t=1405)  
出演者がiPadでTwitterをリロードしまくってコメントを拾う配信がありますが、あの時間無駄じゃないですか?


#環境
Windows10
Python3.7.6

後述するtkinterの"-transparentcolor"がかなり環境依存なため、Linux,Mac環境では別の記法で実現する必要があります。これらの環境の人は適宜ググってください()


#コード全体
清書も推敲もしていない動けばいいや魂のスパゲッティが[ココ](https://github.com/T3aHat/JikkyoAlways)に転がっています。
selfをしっかり理解したい今日この頃。

#大まかな流れ

1. TwitterAPIに登録し、ConsumerKey,ConsumerSecret,AccessToken,AccessTokenSecretを取得する。

2. tkinterで透明なFrameを作る

3. Tweepyで任意の検索ワードでの検索結果を取得し、取得結果の文字列に適当な処理を施したものをLabelのtextとしてある座標に配置する。

4. 配置したLabelをnmsごとにx座標を少しずらして再帰的に呼び出すことでコメントが流れる様子を再現する。

5. 約5秒間隔で画面外までコメントが移動している行をみつけ、そこに対し再検索した結果を再配置。


#仕様
* 初回起動時にTwitterAPIのCK,CS,AT,ASをユーザに入力させてconfig.iniに保存。2回目以降はconfig.iniから変数の値を読みだしてTwitterAPIの認証を試みる。
* 2020年3月某日現在、TwitterAPIは15分で180回検索できるので、コメントの取得は5秒に1回行われる。ただし、画面外に流れたコメントが現れるまで定期的に観察して、現れたら即コメントの取得を行う。
* 流れるコメントを左クリックでfavourite,右クリックでRTを行い、それぞれ赤、緑にハイライトされる。両方行うと青にハイライトされる。
なお、再度クリックするとハイライトはキャンセル、unfavourite,unRTが行われる。



#解説
(誰が読むんだって感じです。私の備忘録。)

まず、適当な`word=input()`に対し、tweepyを用いて

```
auth = tweepy.OAuthHandler(CK, CS)
auth.set_access_token(AT, AS)  
api = tweepy.API(auth)
results=api.search(q=word,count=num_comment)
```
をしてユーザがTwitterAPIを使用できるか確認する。
失敗した場合は`reAuth()`に移行して認証成功するまでCK,CS,AT,ASを入力させ、成功したら`config.ini`に書き込む。
configparserに関しては

>[pythonプログラムにおける設定ファイル管理モジュール～configparserの使い方と注意点～](https://qiita.com/mimitaro/items/3506a444f325c6f980b2)

を参照。

次に、フルスクリーンでtkinterのFrameを背景が透明になるように生成する。ディスプレイの解像度は以下で取得できる。

```python
from ctypes import windll
ww=windll.user32.GetSystemMetrics(0)#width
wh=windll.user32.GetSystemMetrics(1)#height
```
これを`ttk.Frame()`の`width`,`height`に与えればよい。
透明なFrameは
>[tkinterで背景が透過するFrameを作る【Python】](https://qiita.com/WhiteaHat/items/050b572aad0d1686370b)

で作れる。

コメントが動画の上に流れるようにしたいので、`root.wm_attributes("-topmost", True)`として、rootが最前面にくるように設定。

コメントにURLが含まれていると無駄に長くなってしまうので

>[tweepyでURLを含むツイートを除外する【Python】](https://qiita.com/teahat/items/3417c91cc111e4f16ef3)

のように`exURL()`で除外する。
また、コメントに改行が含まれているとlabelが複数行にわたってしまいコメントが重なってしまうので、`text.replace("\n"," ")`と置換。

コメントの取得結果`results`は最初は`nowdatalist`にコピーし、約5秒ごとにコメントの更新を行う`get_comment()`で書き換える行の要素を`results[i]`に書き換えることで、画面上にあるコメントの管理を`nowdatalist`にて行っている。

###get_newcomment()
画面外に流れていったコメントの行に新たなコメントを再配置する関数。
リスト`lefted`(左のleftにedをつけるセンスのなさ())はあるコメントが画面外(ある`label`のx座標<0)に移動すると`True`にする。
`get_newcomment()`実行時に`lefted`内に一つでも`True`があれば、その数だけTwitterAPIを叩いてコメントを取得して`results`に保存。
APIを叩く際に、`since_id`を指定することで、その[ツイートid](http://docs.tweepy.org/en/v3.5.0/api.html#API.search)よりもidの値が大きいツイート群から検索してくれる。
なお、TwitterAPIの仕様上は5秒に1回APIを叩けるのだが、余裕をもって5.05秒に一回実行するようにした。

###update_comment()
`get_newcomment()`で取得した`results`をもとに実際に画面の書き換えを行う関数。
`if(k!=0 and k%2==0):`は、取得したコメントを一気に流さずbreakするために使用。すなわち、1秒に2コメントずつ流れる。
`label`を書き換える際に、`labels[i].place_forget()`として、前の`label`を削除するのがポイント。これをしないと、`Frame`に前の`label`が残り続けてしまう。

###$pyinstaller jk.py --onefile
最後に、このプログラムをexeファイル化して完成。

#関連記事
本プログラムを作る際に色々と勉強したので、備忘録がてら書いてみました。

[tkinterで背景が透過するFrameを作る【Python】](https://qiita.com/teahat/items/050b572aad0d1686370b)
[tweepyでURLを含むツイートを除外する【Python】](https://qiita.com/teahat/items/3417c91cc111e4f16ef3)
[tkinterで作った透過Frameのpyinstallerでの挙動の違い【Python】](https://qiita.com/teahat/items/4aa4611fee3e9ea10bdc)
[tkinterでLabelを動かす【Python】](https://qiita.com/teahat/items/a1e70621a62ab3a42ad8)