Python
Pepper

Pepper用Tweetボックスをつくる

More than 3 years have passed since last update.

Pepperでモーションを作ったり会話を作ったりしているとそれはそれで非常に面白い感じがするのですが、やっぱりソーシャルなネットワークと接続したくなってしまいます。

PythonボックスでPythonを書いちゃえばいろいろできるわけですが、公式ドキュメントをみても、外部のライブラリをimportする方法が書いてない・・・
と思ったらHOW TO IMPORT PYTHON FILES IN YOUR PEPPER APPS?という記事があったので試してみることにします。

Twitter Tweetボックスの作成

まずはじめに何が必要そうかのアタリをつけてみます。
Python で Twitter API にアクセス など読ませていただくに、Twitterへのアクセスには、以下の要素が必要になりそう。

  • Twitter APIへのアクセスに必要なキー類
    • Consumer Key
    • Consumer Secret
    • Access Token
    • Access Token Secret
  • Twitter APIに投稿したい内容
    • ステータス(メッセージ)文字列

また、スクリプトからインポートされるライブラリとして、以下が必要になるかなと。

これらを踏まえて、以下のようなTwitter Tweetボックスを実現することを考えていくことにします。

tweet-design.png

Tweetボックスの仕様としては、

  1. 入力onStartは文字列型で、ツイートしたい内容messageを受け取る
  2. onStartに入力がされると、ツイート処理がおこなわれる
    1. 成功した場合はonStoppedが出力される。値はなし(「バン」タイプ)
    2. 失敗した場合はonFailedが出力される。ステータスコード(数値)が渡される
  3. Twitter APIへのアクセスに必要な Consumer Key, Consumer Secret, Access Token, Access Token Secretはパラメータによって与えられる
  4. Twitter APIへのアクセスはPythonによって記述し、importするOAuthのライブラリはプロジェクトのファイルとして保存する

という感じ。手順としては、

  1. Choregraphe上でPythonボックスを作る
  2. Pythonスクリプトからimportするライブラリを準備する
  3. Pythonスクリプトを記述する

の順番で試していくことにします。

つくってみる

ボックスの作成

まずは、Choregraphe上でボックスの形を作成していきます。

  1. フローダイアグラム上で右クリックをおこない、[ボックスの新規作成]の[Python...]を選択
    new-python-box.png

  2. 名前に Tweet と入力
    add-new-box.png

  3. onStart入力の型を文字列に変更する。入力 onStart の設定ボタンをクリック[A]し、Typeドロップダウンリストをクリック[B]
    edit-onstart-type-1.png

    すると、タイプの一覧があらわれるので、[文字列] を選択し、[OK]ボタンをクリック
    edit-onstart-type-2.png

  4. 次に、出力にonFailedを追加。出力の 追加[+]ボタンをクリック
    add-onfailed-1.png

    入力と同様に設定ダイアログがあらわれるので、Nameに onFailed と入力 [A]し、Typeを[数]に変更 [B]
    add-onfailed-2.png

  5. パラメータを追加する。変数の 追加[+]ボタンをクリック [A]し、Nameに Consumer Key と入力 [B]、Typeを[文字列]に変更 [C]し、[OK]ボタンをクリック
    add-param-1.png

    [OK]ボタンを押すと、変数リストに Consumer Keyが追加されたのがわかる
    add-param-2.png

  6. 5.と同じ要領で、Consumer Secret, Access Token, Access Token Secretを追加
    add-param-3.png

  7. [OK]ボタンを押すと、フローダイアグラム中に Tweetボックス が作成される
    new-tweet-box.png

これでボックスの外身の準備はOK。

ライブラリの準備

TweetボックスではOAuthの認証のため、以下のライブラリをインポートすることにします。

このライブラリの requirements.txt を見ると、以下のライブラリも必要であることがわかったので、これらも含めることにします。

本来はパッケージ管理ツールとかちゃんと使うべきかなと思いつつ、まずはえいやで、以上3つのライブラリを使用してみる感じで、以下の手順でプロジェクトにライブラリのファイルを組み込んでみます。

  1. requests-oauthlib, requests, oauthlib の3つのリポジトリからファイルを取得
    git-download-zip.png

    今回は [Download ZIP] を選択し、リポジトリ内のファイルをZIPでダウンロードする。

  2. 適当なフォルダに lib フォルダを作成し、ダウンロードしたZIPファイル3つを展開したものを以下のような構成にまとめる

    • lib
      • requests_oauthlib
        • __init__.py
        • ...
      • oauthlib
        • __init__.py
        • ...
      • requests
        • __init__.py
        • ...
  3. [プロジェクトの内容]パネルの追加[+]ボタンをクリックし、[フォルダーのインポート...]を選択
    import-folder.png

  4. [インポートするフォルダーを選択]ダイアログが開くので、2.で作成した lib フォルダを選択し、[フォルダーの選択]ボタンをクリック
    import-folder-dialog.png

  5. ファイルリストにlibディレクトリが追加されるので、ディレクトリツリーを展開し、ファイルがインポートされていることを確認
    imported-lib.png

これで、プロジェクトのファイルとしてPythonのライブラリをインポートすることができた(はず)。
次に、TweetボックスのPythonスクリプトとして、これらのライブラリを使用してツイートするようなコードを記述していきます。

Pythonスクリプトの記述

Tweetボックスをダブルクリックしてスクリプトエディタを開き、以下のスクリプトを貼りつけます。

class MyClass(GeneratedClass):
    def __init__(self):
        GeneratedClass.__init__(self)

    def onLoad(self):
        self.framemanager = ALProxy("ALFrameManager")
        self.folderName = None

    def onUnload(self):
        import sys
        if self.folderName and self.folderName in sys.path:
            sys.path.remove(self.folderName)
        self.folderName = None

    def onInput_onStart(self, p):
        import sys, os
        self.folderName = os.path.join(
                    self.framemanager.getBehaviorPath(self.behaviorId), "../lib")
        if self.folderName not in sys.path:
            sys.path.append(self.folderName)
        for moduleName in os.listdir(self.folderName):
            # モジュールのreload
            if moduleName in sys.modules:
                self.logger.info("Loaded: %s, %s" % (moduleName, sys.modules[moduleName].__file__))
                reload(sys.modules[moduleName])
        from requests_oauthlib import OAuth1Session
        self.logger.info("ツイートしています... %s" % p)

        url = "https://api.twitter.com/1.1/statuses/update.json"

        # ツイート本文
        params = {"status": p}

        twitter = OAuth1Session(self.getParameter("Consumer Key"),
                        self.getParameter("Consumer Secret"),
                        self.getParameter("Access Token"),
                        self.getParameter("Access Token Secret"))
        req = twitter.post(url, params = params)

        if req.status_code == 200:
            self.logger.info("OK")
            self.onStopped()
        else:
            self.logger.warn("Failed: %d" % req.status_code)
            self.onFailed(req.status_code)

    def onInput_onStop(self):
        self.onUnload() #it is recommended to reuse the clean-up as the box is stopped
        self.onStopped() #activate the output of the box

コードのポイントとしては・・・

  1. まず、onLoad時にALFrameManagerへの参照(プロジェクト内ファイルのパス解決に利用)取得と、変数の初期化を実施

    onLoad
     def onLoad(self):
         self.framemanager = ALProxy("ALFrameManager")
         self.folderName = None
    
  2. onStartの処理開始時に sys.path によって [プロジェクトの内容]に追加したPythonライブラリをインポート可能に

    onInput_onStart
     def onInput_onStart(self, p):
         import sys, os
         self.folderName = os.path.join(
                     self.framemanager.getBehaviorPath(self.behaviorId), "../lib")
         if self.folderName not in sys.path:
             sys.path.append(self.folderName)
    
  3. プロジェクト内のPythonライブラリをロードし、処理をおこなう

    onInput_onStart
        from requests_oauthlib import OAuth1Session
        self.logger.info("ツイートしています... %s" % p)
        ...
    
  4. 処理完了後、Twitter API呼び出しの戻り値により出力を呼び出す

    onInput_onStart
        ...
        if req.status_code == 200:
            self.logger.info("OK")
            self.onStopped()
        else:
            self.logger.warn("Failed: %d" % req.status_code)
            self.onFailed(req.status_code)
    
  5. アンロード時、 sys.path からプロジェクトのPythonライブラリに関するパスを削除

    onUnload
    def onUnload(self):
        import sys
        if self.folderName and self.folderName in sys.path:
            sys.path.remove(self.folderName)
        self.folderName = None
    

といった感じ。個人的には、 sys.path 書き換えは多少強引な感じもする・・・(アプリケーションごとにこれをやったらConflictしそうだし)
将来的にはpipとかがうまく統合されるとよいなあ、などと思いつつ。

動作確認

作成したTweetボックスを試すため、以下のようなアプリケーションを作ってみる。

  1. standardボックスライブラリの Data Edit > Text Edit を配置
    test-box-1.png

  2. ボックスを以下のようにつなぐ
    test-box-2.png

  3. Tweetボックスのパラメータに、Twitterから取得した Consumer Key, Consumer Secret, Access Token, Access Token Secret を設定
    test-box-3.png

  4. Text Editボックスにツイートしたい内容を入力
    test-box-4.png

これで、任意のメッセージをツイートするフローを作成できた。Pepper実機もしくはバーチャルロボット(Macのみ)で動いた。
Windowsのバーチャルロボットでは実行不可。sslモジュールが有効でないっぽい・・・

こんな感じで、Pepperにツイートさせたりできる。カメラの画像を撮ってツイートとかできると、ハッカソン実況の仕事もPepperに任せられるな・・・などと思いつつ。今後も何かネタができたら書いていきます。