VOICEROID2(紲星あかり)をプログラムから動かしてみる(改良版)


やりたいこと

VOICEROID2(紲星あかり)をプログラムから動かしてみる
https://qiita.com/Teara/items/936733c9e7e47b5ebe79

の改善を行います。
記事最後に記載した問題点の二つを改善します。

  • 改行に対応できない(webhooksとBeebotteの連携時の問題)   ←コレ
  • 数時間後にしか話してくれない(TwitterとIFTTTの連携の問題)  ←コレ
  • VOICEROID2のウィンドウが必ずアクティブになる(コードの問題)

何をやるのか

結局はIFTTTの仕様上できない、Beebotteの仕様上できない というところが二つの問題の原因ですので、IFTTT・Beebotteを使わなければよいということです。

なので、TwitterStreamingAPIを用いて自分でメンションを取得すればいいわけです。

Twitter StreamingAPIからメンションを取得する

言語はPython3でやります。(VOICEROID2を動かすコードもPython3のため)

モジュールを取得

今回はtweepyを使います。

Tweepy Documentation
http://tweepy.readthedocs.io/en/v3.5.0/

pip3 install tweepy

StreamingAPIについて

最初はUserStream使おうと思ってたんですが、2018/6に廃止されてしまうそうなので、今から使っても・・・とことで別の方法を使います。

Twitter上でより良い顧客エンゲージメント体験のための新機能
https://blog.twitter.com/developer/ja_jp/topics/tools/2017/aaa.html

今回はstatuses/filterを使います。

POST statuses/filter — Twitter
https://developer.twitter.com/en/docs/tweets/filter-realtime/api-reference/post-statuses-filter

Streamからフィルターをかけて任意のツイートを取得するサービスです。(とてもざっくり)

コードを書きます

Twitter_Streaming.py
# -*- coding: utf-8 -*-
import tweepy
import re
from VoiceRoid2_Automate import talkVOICEROID2
import time

# API用のキー
CUSTOMER_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXX"
CUSTOMER_SECRET = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ACCESS_TOKEN = "XXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
ACCESS_TOKEN_SECRET = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

# 1.override StreamListener 
# StreamingListenerをオーバーライド
class ReplyStreamListener(tweepy.StreamListener):
    # Statusが届いたら確認する
    def on_status(self, status):
        # 対象に対するリプライ?
        if str(status.in_reply_to_screen_name) == api.me().screen_name:
            # リプライ者とその文言を取得する
            name = status.user.name
            text = status.text
            # 文言から「@ユーザ名」を除く
            text = re.sub('@[A-Za-z0-9_]+','',text)
            # 文言からURLを抜く
            text = re.sub('https?://[a-zA-Z0-9./\-=]*','',text)
            # 話す文言を作る
            phrase = "{0}さんからリプライです。\n{1}".format(name,text)
            # 出力
            print (phrase)
            # VOICEROID2に指示を出す
            talkVOICEROID2(phrase)
            print ('sleep:{0}s'.format(len(phrase)/5))
            time.sleep(len(phrase)/5)
            print('sleep_end')

    # エラー発生時
    def on_error(self,status_code):
        print('ErrorCode:{0}'.format(status_code))
        return True

    # タイムアウト発生時
    def on_timeout(self):
        print('Timeout Occured...')
        return True


# OAuthの準備
auth = tweepy.OAuthHandler(CUSTOMER_KEY,CUSTOMER_SECRET)
auth.set_access_token(ACCESS_TOKEN,ACCESS_TOKEN_SECRET)

# TwitterAPIハンドル取得
api = tweepy.API(auth)

# 2.ストリームの作成
replyStreamListener = ReplyStreamListener()
replyStream = tweepy.Stream(auth = api.auth, listener = replyStreamListener)

# 3.ストリームの開始
# statuses/filterのfollowで選別する
replyStream.filter(follow = [api.me().id_str])

公式ドキュメントを見たら大体わかるので見てみましょう。

Tweepyでストリーミング
https://kurozumi.github.io/tweepy/streaming_how_to.html

①クラスをオーバーライドして、Statusが来た時の処理とエラー処理を定義する
②ストリームを作成し、オーバーライドしたクラスを指定する
③ストリームを開始する
の3Stepで行います。
今回はfilterのfollowで自IDを指定することで、自分に関するstatusを取得し、その中で自分に対するリプライがあれば処理を行うようにしています。

自分はstatusの中身について躓いたのでちょっとだけ書きます。
tweepyのストリームできたstatusはjsonはパースされています。通常、パース語のjsonはPHPの連想配列のような形で取得します(status['name']とか)が、tweepyのstatusはstatus.nameという形で取得します。(statusの中身は一度statusを出力するとわかります)
いつもと違う感じで取得することになるので、気を付けましょう。

今後

のこりの一つの問題ですが解決が難しそうですね・・・
適当なパソコンをサーバみたいにして、Active取られても問題なくしてもいいのかなぁと(何も解決していませんが・・・)
何かご存知の方いらっしゃったらご教授いただけたら嬉しいです・・・