日記とか、工作記録とか

自分に書けることを何でも書いてゆきます。作った物、買ったもの、コンピュータ系の話題が多くなるかもしれません。

Logicool G602マウスのチャタリングの修理(解析編)

(あとからつけた)目次

  1. (今回)Logicool G602マウスのチャタリングの修理(解析編) - 日記とか、工作記録とか
  2. Logicool G602マウスの修理(挫折編) - 日記とか、工作記録とか
  3. Logicool G602マウスの修理(ゾンビのように復活編) - 日記とか、工作記録とか

自宅のPCではいわゆるゲーミングマウスを長いこと使っています。ゲームをそれほどするわけではないのですが、補助ボタンがたくさん付いていると、ブラウザの進む戻るとか、ウインドウを閉じるなどよく使う操作をショートカットできて便利です。今使っているのがLogicool G602です。無線マウス、適度な重さ、大き目のボディは掴みやすいなど気に入っています。

が、マウスは消耗品です。何年使ったか覚えていませんが、左クリックの調子がおかしくなってきました。普通にクリックしたはずなのにダブルクリックとして扱われます。いわゆるチャタリングノイズです。以前Raspberry piの回路にスイッチを設置した時にはプログラム側からこれを防止しました。

windvoice.hatenablog.jp

マウスは消耗品ということで、このマウスを買い換えました。同じものがまだ販売していたので再度注文。めでたしめでたし…… だったのですが、よく考えたらもしかして直せるのではないかと思いつきました。今回はマウスの分解です。マウスは裏のツルツルしたソールを剥がすと、固定ネジが出てきます。これを外すと、本体が上下に分かれて基盤が出てきます。

f:id:WindVoice:20160827224956j:plain

クリック部分のスイッチはこんな配置です。

f:id:WindVoice:20160827225354j:plain

左右のクリックは同じ型番のスイッチでした。ここのWebサイトを参考に分解しましたが、部品変わっていませんね。数字の刻印が違うのはシリアル番号でしょうか。

f:id:WindVoice:20160827225657j:plain

分解する前は、ホコリなどでスイッチの接触がおかしくなっているのでは、と想像していたのですが、本体の中は綺麗なものでホコリが原因とは考えられませんでした。となるとスイッチの摩耗などを疑うことになるのでしょうか。スイッチを交換できるか型番で探してみたところ、なんとAmazonにこんなものまで置いてあるんですね…… 驚きです。

代わりのマウスは買ってしまいましたが、それほど高い部品でもないので交換にチャレンジすることにします。現在部品の到着待ちです。もしうまくいかなかったら、次はレーザ発信器を使って何か遊びを考えることにします。

Javascriptでチェスクロック(対局時計)を作成しました

今回はRaspberry piからは離れてJavascriptなど……

前置き

趣味で少し将棋をやるのですが(もっぱら見る将ですが。最近はアゲアゲさんのチャンネルなど……)、将棋やチェスでは持ち時間があり、この時間内に対局を終わらせることが求められます。例えば持ち時間10分であれば、一局100手あまりの対局の時間配分を考えて、あまり悩まずに指し続けなければなりません。時間切れになったらそこで「切れ負け」です。

ルール的には持ち時間が尽きたらその後は一手30秒、などの方法がとられることもありますが、いずれにせよこれを管理する対局時計というものが必要になります。例えばこんなの。

これはよく使われている一般的なもので「お願いします」なんて自動音声がでたり、秒読みをしてくれたりもする高機能な製品です。便利ですしおそらくボタンも丈夫に作ってあることでしょう(※自分の手を指したら対局時計の上部にあるボタンをパン、とたたくと相手方の時間が減り始める仕組み。けっこうぞんざいに扱われる宿命のボタン)。そして結構いいお値段もします。きっと大量生産できないので単価が高かったり、そもそもネット将棋ならこれは必要なかったりで、そんなに売れないのかと思います。

それでも作ってみる?

仕組みはそれほど難しいものではないので、jQuery + Javascriptでごりっと作ってみました。Amazon Web Services(AWS)にある自分のサーバに置いてあります。安易ですが「ちぇくろ」と名前をつけました。

ちぇくろ

画面表示はこんな感じです。シンプル・イズ・ベター。

f:id:WindVoice:20160809132309p:plain

最初の設定画面だけAmazonのバナーがでますが、対局中は非表示ですのでご容赦を…… だって遊び用のインスタンスとはいえ、料金も少しかかっていますからね。ナニトゾナニトゾ。

特にiPhone / iPadなどのモバイルで便利と思います。将棋盤の隣にこれを置いておいて、一手さしたら画面をタップ、という感じで使います。タップするときは落ち着いてやらないと、スワイプと判断されてうまく動かなかったりしますのでご注意ください。今後気が向いたらアップデートをしようとおもいます。

スペシャルサンクス

プログラムは自前で書けるのですが、音とかフォントはふぉんとにどうにもならないのでお借りしています。
些細なリンクで済みませんがせめてもの感謝ということで……

秒読みの効果音はこちらから
 ⇒ select02.mp3 / select07.mp3 / blip01.mp3

タップしたときの効果音は効果音ラボさんから
 ⇒ cursor8.mp3

残り時間表示のための7セグフォントは消し缶詰-keshikan.netさんから
 ⇒ DSEG7Classic-Bold.ttf

追記

Android端末をお持ちの方は動作確認して頂けると助かります。私は持っていなくて確認がとれていません。iPhoneSafariでも動いていますし、jQueryは互換性も考えられているはずで、たぶん大丈夫とは思うのですが。

Raspberry pi + Twitter : 謎の例外が飛んできても動揺せずにしゃべり続ける

動作を安定させるために

しばらく動かしたままにしておいた @RaspiParrot ですが、いつの間にか止まっていました。

twitter.com

何か動作不具合が起こっているに違いないのですが、ログではTwitterにアクセスしたところで突然死んでいることしかわかりません。Python-TwitterモジュールはTwitter APIのエラーが発生した場合にTwitterError Exceptionをraiseするようになっており、私のプログラムも例外処理をしているのですが、今回は突然停止していることからそれ以外の例外が発生しているとみられます。このままでは原因追求ができないので、標準エラー出力をファイルに取得しておきました。

pi@pi3:~/twitter$ more boot.sh 
#!/bin/bash

./RaspiParrot.py 1>> log/stdout.log 2>> log/stderr.log &

pi@pi3:~/twitter$

で、今回停止した時に取得された標準エラー出力がこれ。実際にはもっと長いのですが要点を抜粋です。

Traceback (most recent call last):
  File "./RaspiParrot.py", line 201, in CheckMentions
    for state in api.GetMentions():
  File "/usr/local/lib/python3.4/dist-packages/twitter/api.py", line 3430, in GetMentions
    resp = self._RequestUrl(url, 'GET', data=parameters)
  File "/usr/local/lib/python3.4/dist-packages/twitter/api.py", line 4772, in _RequestUrl
    resp = requests.get(url, auth=self.__auth, timeout=self._timeout)
  File "/usr/lib/python3/dist-packages/requests/api.py", line 60, in get
    return request('get', url, **kwargs)
  File "/usr/lib/python3/dist-packages/requests/api.py", line 49, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 457, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/lib/python3/dist-packages/requests/sessions.py", line 569, in send
    r = adapter.send(request, **kwargs)
  File "/usr/lib/python3/dist-packages/requests/adapters.py", line 407, in send
    raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', gaierror(-2, 'Name or service not known'))

つまり接続エラーな訳ですね。今のところ、DHCPIPアドレスの解放と再取得の動作が怪しいと感じていますが、原因追求のためにもう少し細かいエラーを取得することにします。他の問題も起こるかもしれませんし。こんな感じで未知のExceptionも最低限ログが残るようにします。

    try:
        api = twitter.Api(consumer_key=consumer_key,
                       consumer_secret=consumer_secret,
                       access_token_key=access_key,
                       access_token_secret=access_secret,
                       input_encoding=encoding)
        logger.info(u"GetAPI:正常終了")
    except TwitterError as e:
        logger.warning(u"twitter.Api error: %s" % ( e.message ))
    except Exception as e:
        logger.warning(u"Exception[0]%s [1]%s [2]%s" % (sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))

これで再始動して様子見です。少なくとも、エラーが発生してもそのまま死なずにリトライすることを期待しています。さて、何が釣れますやら……

オウムらしからぬ適当なことを喋る

目次 [Raspberry pi 3 + Python = Twitter Bot]

  1. Twitterの開発者登録からツイートをポストするまで
  2. Python-Twitterモジュールの情報まとめ
  3. メンションをもらったら返答するようにした
  4. 異常終了の原因を追及できるように例外の処理を追加
  5. マルコフ連鎖モジュールのPython3対応作業

前回までに、マルコフ連鎖に関連するモジュールをいくつか改造しまして、動作するようにしました。これは、ツイートを自動生成する処理の基板にあたるものです。今回はようやく独自のツイートを生成して投稿するところを作ろうと思います。

つくっていて急に心配になったのですが、オウムも場合によっては人語を喋りますよね。九官鳥じゃなかったかと心配になりました。他所様の動画ですが、これすごいですね。オウムが笑うと子供がつられて笑うので、ますますオウムが笑うようになるという…… 恐ろしい連鎖です。

youtu.be

ツイート生成の方針

これまでに、トレンドワードを取得してリストにするところまではできていました。これをツイートにまとめて投稿することもできます。

f:id:WindVoice:20160724212621j:plain

トレンドを取得すると、キーワードだけでなく、関連する情報もついてきます。キーワードは trend.name という名称で取り出せるのですが、他にも trend.url という名前で、関連ツイートを検索するURLが取得できます。このURLをSearchして、他のツイートをまとめ、それをマルコフ連鎖モジュールで「学習」したあと、自分なりのツイートを生成することにします。できあがりはこんな感じです。

f:id:WindVoice:20160724212910j:plain

ただ、取得したツイートを学習させるだけだと、他の方のツイートをそのままパクツイしたようなものも生成されてしまいます。それに他人を中傷するツイートも少なからずあるので、安易にマネしてしまうのは危ないこともあります。そんなわけで、カタカナに展開することでインパクトを和らげることにしました(まぁ、オウムですし……(逃避))。ただ、全部カタカナだと何を言っているのかわからないので、トレンドワードそのものは残しておくことにしています。

twitter.com

技術的なところ

マルコフ連鎖は、前回Python3対応の作業をした python-markovchains と、その前提モジュールとなる py-extractword を使用します。

具体的には、こんな流れになりますね。あとはひとつひとつ地道に作っていくだけです。

  • 地域の指定を「日本」にしてトレンドワードを取得(twitter.GetTrendWoeid)
  • trend.url を使用してトレンドワード関連ツイートを取得(twitter.GetSearch)
  • ツイート群からテキストを取り出し(status.text)、マルコフ連鎖の邪魔になるゴミを取り除く
  • テキストを全部連結してmarkovchains.analyze_sentence()で学習させる
  • markovchains.make_sentence()で文章を作成する。ほどよいのができるまでリトライ
  • MeCabを-Oyomiオプションで起動して、読み仮名を取得する
  • できあがったツイートを投稿

今回のハマリポイント

markovchains.make_sentence()が、無限に長い文章を生成して応答を返さなくなることがあります。プログラムがCPUを100%使用する状態のまま止まってしまい原因を探るのに苦労しました。そういうときはpython -m pdbを使用してデバッガから動作を確認するのが早道かとおもいます。

この問題は、python-markovchainsのmake_sentence()関数の中に処理を追加して対処しました。ツイッターではどうせ140文字を超えたらツイートできないのですから、漢字を含めて100文字くらいで終わってくれないと、使えません。ある程度長くなったところで諦めて結果を返すように動作変更しました。GitHUBからforkしたのは今回が初めてですが、やってみるものですね。

次回予告?

気の向くままに作業していますが、それでもフォローしてくれる人がいるので、フォローが届いたらフォロー返しをしようかと思います。多くのBotはこちらから積極的にフォローしていきますが、そこまでやっても得るものがあるかどうか…… しばらくはやめておきましょう。

Raspberry Pi 3 Model B (Element14)

Raspberry Pi 3 Model B (Element14)

Raspberry pi 3 + Twitter [RaspiParrot] 他のプログラムのPython3対応作業

目次

  1. Raspberry pi 3 + Python = Twitter Botの起動(とりあえず投稿するところまで)
  2. Python Twitterのメモ
  3. Raspberry pi 3 + Python = Twitter Bot(Mentionをとりあえず返せるところまで)
  4. 例外の処理を追加

現在の作業目標は、Twitterのトレンドワードに関連したツイートを多数集めて、それらに使われている単語を合成し、何か面白そうなツイートができないかな、というものです。

トレンドワードに関連するツイートを収集

Twitterにはトレンドという情報があります。多くの人がつぶやいているキーワード(ニュースとかテレビ番組とかの話題が多い)がリストになったものです。この情報の入手は簡単で、Twitter APIとしてすでに実装されていますので、割合スムーズに収集することができました。部分的なソースを引用しますが、GetTrendsWoeid()という関数が、Twitter APIの下のマニュアルの機能を呼び出しています。WOEIDというのは、地球上の特定の場所を示す郵便番号的なもののようです。私のソースでは23424856を使っており、これは日本を示すWOEIDです。都道府県単位でも変更できるようです。

dev.twitter.com

def CheckTrendWord(api):
    '''
    時々トレンドを入手してコメントしたりする
    '''
    global trendticker
    try:
        logger.debug(u"trendticker:%s" % ( trendticker ))
        if trendticker != 0:
            trendticker -= 1
        else:
            trendticker = int(inifile.get("trend","frequency"))
            # woeid = 23424856は、日本のトレンドワード
            trend = api.GetTrendsWoeid(woeid=23424856)
            # トレンドに関連したツイートを調査
            AnalyzeTrendTweet(api, trend)
            # トレンドワード一覧をツイート
            PostTrendWord(api, trend)
    except TwitterError as e:
        logger.warning(u"CheckTrendWord Error:%s" %( e.message ))

で、とりあえず @RaspiParrot はトレンドワードを並べて定期的にツイートする機能を実装しています。Botにありがちな機能ですね。昨日はポケモンGO関係のキーワードばかりになっていてテストとしてはイマイチな感じでした。

f:id:WindVoice:20160723225643p:plain

しかし、正直これだけだと、フォローしたいアカウントとは言えないですね。もっと面白くしなければ……

独自のツイートをつぶやかせたい

マルコフ連鎖、という技術があります。たくさんの既存の文章から、単語と単語のつながりに関する情報を学習して、ある単語の後には別のある単語が来ることがある…… といった情報を集積することで、今度はその情報をもとになんとなく意味が通りそうな文章を生成できるというものです。単語と単語のつながりしか見ていないので、文章や文脈の全体としては、まとまった内容になりません。何も知らずに読んでいると、酔っているか、頭のおかしい人が支離滅裂なことを言っているように見えます。でも、今作っているのはBotですし、名前もオウム(Parrot)ですので、その辺りはご愛嬌ということにしまして、ともかく独自の文章を生成するプログラムの作成を進めることにします。

実は何年も前に書いたマルコフ連鎖のプログラムが手元にあるのですが、Perlで書かれているので、Pythonに書き換えるのも手間がかかります。どうしたものかと思っていたらすでに作成している方がいたので、GitHubにあったソースコードをforkさせてもらうことにしました。

github.com

すぐ使えるかと思ったのですが、Python 2系列で使うことを目的にしていて、そのままでは動作しませんでした。また、MySQLPostgreSQLPythonモジュールがインストールされていないと、これらのデータベースを使わない場合でも動かない、というちょっと困った依存についても排除したかった(だってそれらをインストールするにはまたPython3の壁に突き当たるので……)ので、forkして修正させてもらいました。その私のレポジトリがこちら。

github.com

まだ広汎なテストはできていませんが、少なくともサンプルレベルであれば動きます。前提パッケージになっているpy-extractwordの方もPython3向けに修正が必要でしたのでforkしました。情報を集積するデータベースとしてMySQLPostgreSQL は使いたいのであれば使える(はず)ですが、テストはしていません。私としては、使わずに済ますつもりでいます。

github.com

さらに前提としてMeCabpython-mecabも必要なのですが、こちらもPython3で動かすのに苦労したものの、なんとかなりました。ネットを検索すると他の方の足跡とともに多数の情報が見つかりますので、ここには書きません。必要でしたら探してみてください。

準備完了……? のつもりで次回予告

これで作りたいプログラムの前提になるモジュール類は揃ったと思いますので、ようやくトレンドツイートを収集して、学習して、なんとなく今はやりのツイートっぽい発言をするオウムの作成を目指そうと思います。

Raspberry pi 3でTwitter [RaspiParrot]

#おそるべしPokemon Go…… ってのはさておきまして。

安普請だったのでもう少し堅牢に修正

昨日動かしたままにしておいたRaspiParrotなのですが、謎のエラーで停止していました。

2016-07-21 18:40:40,130 INFO ----- OneCycle開始 -----
2016-07-21 18:40:40,131 INFO Twitter APIの使用を開始します。
2016-07-21 18:40:40,146 INFO Starting new HTTPS connection (1): api.twitter.com
2016-07-21 18:40:40,842 INFO 返答済みのMention:[2016-07-21 17:52:19] WindVoice_en
2016-07-21 18:40:40,843 INFO 返答済みのMention:[2016-07-21 16:21:50] WindVoice_en
2016-07-21 18:40:40,843 INFO 返答済みのMention:[2016-07-21 15:06:49] WindVoice_jp
2016-07-21 18:40:40,844 INFO 返答済みのMention:[2016-07-21 10:32:28] WindVoice_jp
2016-07-21 18:40:40,845 INFO 返答済みのMention:[2016-07-21 09:19:22] WindVoice_jp
2016-07-21 18:40:40,845 INFO ----- OneCycle終了 -----
2016-07-21 18:43:40,945 INFO ----- OneCycle開始 -----
2016-07-21 18:43:40,946 INFO Twitter APIの使用を開始します。
2016-07-21 18:43:40,962 INFO Starting new HTTPS connection (1): api.twitter.com
pi@pi3:~/twitter$

なぜ謎のエラーなのかといいますと、記録が残っていないためです。上のとおり、ログは突然途切れています。サドンデスですね。これでは原因が突き止められないので、まずは情報採取をしっかりやりましょうということで改造します。

例外を処理

Pythonには「例外」という機能があります。何かまずいことが発生したときに、本来のプログラムの段取りとは別に、「突然ですがここで緊急ニュースです」とでもいった調子で始まる処理のことです。どんな「例外」が発生しうるのかはそのときやっている処理によるのですが、Python-Twitterの場合は、"TwitterError"という名称の「例外」が定義されており、これが発生(raise)します。下のリンクはソースコードです。ところどころにraiseという命令が置いてあります。

python-twitter/api.py at master · bear/python-twitter · GitHub

さて、例えばtwitter.api()を呼び出してkeyからアクセス権を取得するとき「アクセスしすぎでしょエラー」が発生することがあります。15分で15回以上アクセスすると、Twitter側でそれ以上の処理を拒否するようです。この場合、しばらく待ってリトライするのが正しい処理ということになりますね。

下のようにtry~catchを使用しました。しばらく待ってリトライするので、ログに残すだけでよいはずです。なお、発生した例外に対応する処理が作られていない場合、プログラムは強制的に終了させられます。RaspiParrotのような常駐プログラムでは、これは致命的ですので注意する必要があります。

def GetAPI():
    '''
    Twitter APIを準備する部分
    '''
    global inifile
    logger.info("Twitter APIの使用を要求")
    consumer_key    = inifile.get("keys", "TWEETUSERNAME")
    consumer_secret = inifile.get("keys", "TWEETPASSWORD")
    access_key      = inifile.get("keys", "TWEETACCESSKEY")
    access_secret   = inifile.get("keys", "TWEETACCESSSECRET")
    encoding        = 'utf-8'
    try:
        api = twitter.Api(consumer_key=consumer_key,
                       consumer_secret=consumer_secret,
                       access_token_key=access_key,
                       access_token_secret=access_secret,
                       input_encoding=encoding)
    except TwitterError as e:
        logging.warning(u"twitter.Api error: %s" % ( e.message ))
    except Exception as e:
        logging.warning(u"不明なエラー")

GitHub更新しました

更新したソースコードGitHubに上げておきました。
github.com

Raspberry pi 3 + Python = Twitter Bot(Mentionをとりあえず返せるところまで)

アカウントを独立する

さて、前回までにとりあえずPython-Twitterモジュールの力を借りて起動するようになったTwitterクライアントですが、BotBotらしくしたほうがいいのではないかということで、独立したアカウントを作成することにしました。

twitter.com

現在は、こちらのアカウントにRaspiParrotという名前でアプリケーションを登録しています。決まったことしかしゃべれないオウムのようなヤツなのでParrotという名前にしています。

ソースコードをひたすら書く

実装したい機能は、処理順に並べて大きく4つあります。

  1. 常時起動のプログラムで、定期的に動作を開始する
  2. 自分に届いたメンション(*1)を取得する
  3. 取得したStatesのそれぞれでループする。すでに返答を返したツイートも取得されるので、返答済みかどうかは判断する必要がある。
  4. まだ返答していないメンションを見つけたら何か応答を返す

(*1) 自分のアカウント名 @RaspiParrot という文字列を含んだツイートのことです

何からやるか?

下調べをした結果をもとに計画を立てました。実際には作りながら計画を立てている感じですが。

まず、自分に届いたメンションを取得するのはPython-TwitterのGetMentions()を使用すれば得られることがわかりました。

得られるのはメンションのリストなので、for文を使ってひとつひとつ確認することにします。メンションには、それが投稿された日時を示すcreated_atやcreated_at_in_secondsという要素を持っているので、いつのメンションまで返答済みなのかを記録しておいて、その時刻よりあとに届いたメンションに返答を返すことにします。

また、このプログラムは相当な回数再起動をすることになるので、どこまで返答済みかは、プログラムの外部に保存しておく必要があります(そうでないと何度も返答がきてうっとうしいことになるので)。コンフィグファイルを作成してそこに記録を残すことにしましょう。

返答を返すのはPostUpdate()というPython-TwitterAPIに任せればよい…… と思ったのですがこれが大変でした(後述)。返答内容は、まずは簡易的にごあいさつを返すだけにしましょう。 @(相手のスクリーン名) + あいさつひとこと、でよいのですが、Twitterは同じ内容のメッセージを連投することはできない制限があるので、場当たり的ですが日時文字列を追加して重複メッセージを避けることにします。

今回のハマリポイント

なんでもない作業と思っていても、毎回何らかのポイントではまります。プログラムを書く力というのは結局はまったときに対処する方法をどれだけ持っているかですね。今回はPython-Twitterのバグでした。api.pyの中でstr()関数の変換のエラーがでています。モジュール内のエラーだと対処に困ります。ということで本家を探してみたところ、既知の問題のようです。

pi@pi3:~/twitter $ ./RaspiParrot.py
Traceback (most recent call last):
  File "./RaspiParrot.py", line 86, in <module>
    main()
  File "./RaspiParrot.py", line 82, in main
    OneCycle()
  File "./RaspiParrot.py", line 62, in OneCycle
    ReplyMention(api, state)
  File "./RaspiParrot.py", line 77, in ReplyMention
    newstates = api.PostUpdate(u"@%s メンションありがとう" % ( state.user.screen_name ))
  File "/usr/local/lib/python2.7/dist-packages/twitter/api.py", line 949, in PostUpdate
    u_status = str(status, self._input_encoding)
TypeError: str() takes at most 1 argument (2 given)

Error in UploadMediaChunked · Issue #345 · bear/python-twitter · GitHub

ここでは、対処済みということになっているのですが、私の手元では問題が再発します。該当のファイルが古いのだろうか、など調べてみたのですがそうでもなく…… 原因がよくわからなかったのですがPython 2.7の互換性の問題だという説明があったので、やむをえずPython3を使って動かすことにしました。

Raspberry piにはPython3がインストール済みでしたが、Python-Twitterは2.7にインストールされていたので、インストールし直しになりました。

  1. /usr/bin/pythonがpython27へのシンボリックリンクになっていたのでいったん削除
  2. python3へのシンボリックリンクを/usr/bin/pythonにリンクしなおし
  3. Python-Twitterをインストール

これでなんとか動くようになりました。

ソースコード

というわけで、できたのがこちらのソース。
同じ物をGitHubにも置いておきます。

github.com

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import twitter
import time
import logging
import configparser

CONFIGFILE="config.ini"

def init():
    '''
    最初に1回だけ処理する部分
    '''
    # 設定ファイルの準備
    global inifile
    inifile = configparser.ConfigParser()
    inifile.read(CONFIGFILE)

    # ログファイルの準備
    global logger
    logging.basicConfig(filename=inifile.get("log","filename"),
                        level=logging.INFO,
                        format='%(asctime)-15s %(levelname)s %(message)s')
    logger = logging.getLogger("RaspiParrot")

def GetAPI():
    '''
    Twitter APIを準備する部分
    '''
    global inifile
    logger.info("Twitter APIの使用を要求")
    consumer_key    = inifile.get("keys", "TWEETUSERNAME")
    consumer_secret = inifile.get("keys", "TWEETPASSWORD")
    access_key      = inifile.get("keys", "TWEETACCESSKEY")
    access_secret   = inifile.get("keys", "TWEETACCESSSECRET")
    encoding        = 'utf-8'
    return twitter.Api(consumer_key=consumer_key,
                       consumer_secret=consumer_secret,
                       access_token_key=access_key,
                       access_token_secret=access_secret,
                       input_encoding=encoding)

def OneCycle():
    '''
    定期的に、繰り返し処理する内容
    '''
    global inifile
    logger.info(u"----- OneCycle開始 -----")
    LastMentionSeconds = int(inifile.get("records", "LastMentionSeconds"))

    api = GetAPI()

    try:
        MaxMentionSeconds = 0
        for state in api.GetMentions():
            logging.debug(u"LastMentionSeconds:%s" % ( LastMentionSeconds ))
            logging.debug(u"created_at_in_seconds:%s" % ( state.created_at_in_seconds ))
            t = time.strftime(u"%Y-%m-%d %H:%M:%S", time.localtime(state.created_at_in_seconds))
            if LastMentionSeconds < state.created_at_in_seconds:
                 logger.info(u"新しいMentionが到着:[%s] %s" % (t, state.user.screen_name) )
                 # このMentionは未処理なので応答を返す
                 ReplyMention(api, state)
                 if MaxMentionSeconds < state.created_at_in_seconds:
                     MaxMentionSeconds = state.created_at_in_seconds
            else:
                 logger.info(u"返答済みのMention:[%s] %s" % (t, state.user.screen_name) )
        # メンション応答があった場合はLastMentionSecondsを更新
        if MaxMentionSeconds != 0:
            inifile.set("records", "LastMentionSeconds", str(MaxMentionSeconds))
            inifile.write(open(CONFIGFILE, 'w'))
    except UnicodeDecodeError:
        print("Your message could not be encoded.  Perhaps it contains non-ASCII characters? ")
        print("Try explicitly specifying the encoding with the --encoding flag")
        sys.exit(2)
    logger.info(u"----- OneCycle終了 -----")

def ReplyMention(api, state):
    '''
    Mentionが届いている。何か気の利いた返答をしよう。
    ToDo: ちっとも気が利いていないので何か考える
    '''
    logger.info(u"Mention返しを開始")
    t = time.strftime(u"%Y-%m-%d %H:%M:%S", time.localtime())
    message = u"@%s メンションありがとう[%s]" % ( state.user.screen_name, t )
    newstates = api.PostUpdate(message, in_reply_to_status_id=state.id)

def main():
    '''
    メインルーチン
    '''
    init()
    while True:
        OneCycle()
        # Twitter API Rate Limitにより、15回/15分の制限がある
        time.sleep(180)

# おまじない
if __name__ == "__main__":
    main()

動作します

今のところごく簡単なありがとうメッセージを返すだけです。フォローする必要はありません(しても、何も起こりません)。@RaspiParrotにメンションが届くと、こんな簡単なメッセージが返ってきます。開発中でなければ、動き続けているはずです。

f:id:WindVoice:20160721173135p:plain

今後

あとはなんといっても気の利いた返答ですね。ここがアイディアの絞りどころなわけですが…… できそうなことで何かあるかなぁ。検討中です。