たまめ的生活記録

日々で思ったこととか、技術的なこととか。

むきりょくbotを復活させた。【Python: ツイート編】

高校生の頃に作って、TwitterAPI仕様が変わり止まっていたむきりょくbotを5年ぶりに再稼働させました。
前回の記事はこちら。

june06-lifelog.hatenablog.com

セリフの登録

むきりょくbotむきりょくかん。さんの作品に登場するキャラクターのセリフや文章を呟くbotです。そのためにはセリフがないと始まりません。 セリフしかもつ必要はないので、今回は単純にテキストファイルで保持するようにしました。以下のように、1行につき1つのセリフが書かれているイメージです。

吾輩は猫である。名前はまだない。(吾輩は猫である/夏目漱石)
クラムボンは笑ったよ。(やまなし/宮沢賢治)
ある日の暮方の事である。一人の下人げにんが、羅生門らしょうもんの下で雨やみを待っていた。(羅生門/芥川龍之介)

本当はむきりょくbotを作った当時のPCにテキストファイルが残っていればよかったんですが、消失していました。仕方ないので再読を兼ねてひたすらゲームをプレイしたり、過去のツイートを遡って追加していきました。ちなみに、ツイートを遡る時はクエリで日付を指定すると便利です。

from:mukiryokuBOT until:2013-06-20 (2013年6月20日以前のむきりょくbotのツイートを取得)

セリフのランダム取得

セリフを登録したら、今度はそのセリフの中から呟くセリフを取得する必要があります。
ファイルを一度に開いてランダムに抽出すると今後セリフが膨大な数になった時とか嫌なので、いい感じのアルゴリズムをないかと探しました。するとstackoverflowにいい感じのものがあったので参考にさせていただきました(記事中の(Waterman's "Reservoir Algorithm") from Knuth's "The Art of Computer Programming")。

How do I read a random line from one file in python? - Stack Overflow

ツイート

さて、これでセリフは取得できたのであとはツイートするだけです。Tweepyではupdate_status()メソッドたったひとつでツイートできます。

import oauth # oauth認証編で作成したもの
import tweepy

def post_speech():
    api = oauth. create_twitter_api()
    speech = get_speech()
    api.update_status(speech)

def get_speech():
    ~
    Reservoir Algorithmでランダムにセリフを取得
    ~

このプログラムをtweet.pyとして保存し、シェルから実行すれば呟くことができます。

$ python tweet.py

f:id:juntama13:20190105011916j:plain:w700

次はどうせなので、何かしらエラーが起きてツイートできなくなった時にslackへ通知が来るようにします。

Pythonのos.getenvとos.environ.getの違い

何だろうと思って調べてみました。
結論、違いはないみたいです。備忘録的にここに残しておきます。

参考にした記事 qiita.com

もはやただのaliasでした。
単純に環境変数を取得するのであれば、個人的にはos.getenvの方が短いし好きですね。

むきりょくbotを復活させた。【Python: OAuth認証編】

高校生の頃に作って、TwitterAPIの仕様が変わって止まっていたむきりょくbotを5年ぶりに再稼働させました。

このbotを作った当時はCを授業で少し書き始めたくらいでプログラミングがほぼできず、かつAPIとか何それという状態だったので、「Twitter bot ツール」とかで出てきたツールを使って作ったような記憶があります(たしかPHPだった)。それを@pagesかなんかでバッチ処理してたんじゃなかったかなぁ。

その後TwitterAPIが変わったか@pagesのサービスが終わったかでずっと止まってました。 直さなきゃ直さなきゃと思いつつもなかなか直せずにいて、先日知人から「あれ最近見ないね」と言われてやっと直すことを決意しました。

完全に0から作ることになりましたが、ただセリフを呟いていくだけなのでだいたい1日で完成しました。諸々の管理上リポジトリは公開できませんが、どんな感じの処理をしているかは書いて行こうと思います。

botの構成

言語:Python
ライブラリ:Tweepy
デプロイ環境:Heroku

トークン取得

TwitterAPIを使うための申請方法やトークンの取得は色々記事があるので、そちらを参考にしてください。 僕はこれらを参考にしました。

TwitterのAPIについて
Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2018年9月時点の情報 - Qiita
【第1回】Twitter APIを使うためにdeveloper accountの申請をしよう! – cmblog

OAuth認証

今回の言語はPythonで、ライブラリはTweepyを使用しました。
TweepyはTwitterAPIをよしなに扱えるようにしてくれるライブラリで、非常にメジャーです。これを使ってOAuth認証し、APIオブジェクトを作成します(※トークン等はそれぞれ環境変数として設定している前提です)。

このcreate_twitter_api関数を呼び出してAPIオブジェクトを取得することで、各メソッドが使えるようになります。
実際に使用するのはまた別の記事で。

続き

GoでSlackのIncoming Webhooksを使う

Slack Incoming Webhooks

Slackにある機能の1つで、これを使うと外部サービスからSlackにメッセージを送信することができます。

詳しい使い方はこちらにあるので、参考にして見てください。

qiita.com

GoでHTTPリクエストを送る

Incoming Webhooksといっていますが、実際にはHTTPリクエストを送信するだけです。
payloadパラメータにJSON形式でいくつかのフィールドを設定することで、自由に装飾したメッセージを送信することができます。

今回は単純なテキストメッセージを送信するだけに留めています。というのも、もともと僕が運用しているTwitter botでエラーが起きた時に通知するためのものなので、そこまで凝ってないというだけです。

抜粋しただけなのでmain関数にはなっていませんが、このSendSlack関数にstring型のmessageを渡せば、環境変数で設定したチャネルへメッセージを送信できます。

Dockerのno Space Left on Deviceエラーでビルドできなくなった。

先日Docker buildを実行したところ、エラーが出てbuildできなくなりました。
色々調べてみたところ、no Space Left on Device というエラー文を発見。
どうやらVMの容量を食いつぶしてしまっていたみたい。

原因

ぼくが使っていた環境だと、docker-compose up するとPCのファンがガンガン回ってうるさかったので、ずっと docker-compose run でコマンドを実行していました。
それが今回わかったんですが、docker-compose run ってコンテナを新たに立ち上げちゃうみたいですね。完全に勉強不足(というかそもそも勉強していなかった)。

docker ps してみるとここ半年で立ち上げられた山のようなDockerコンテナが。
同じようにdocker images も悲惨なものでした。

対処

ぼくはDockerでは一つの環境しか使っていなかったので、コンテナとイメージを全部削除することにしました。
削除するコマンドはそれぞれ

  • docker rm <コンテナID>
  • docker rmi <イメージID>

ですが、山のようなコンテナをひとつずつ削除していくのは骨が折れるので、以下のコマンドで全部消しました(使っているのはbashです)。

  • docker rm $(docker ps -a -q)
  • docker rmi $(docker images -q)

これで無事ストレージが解放されてbuildできるようになりました。

runの対処

友達に聞いたところ、--rm オプションをつければプロセス終了と同時にコンテナも消えてくれるとのこと。
今度からこれを使っていこうと思います。

参考

qiita.com

48n.jp

Pythonでtry-exceptする引数付きデコレータを単体テスト

先日Pythonの引数付きデコレータをテストしたいということがありました。
結構調べるのが大変だったので、メモっておきます。

引数付きデコレータ

今回テストしたかったのはこんな感じのデコレータです。
何も起きなければデコレートした関数をそのまま実行し、例外が起きた時に引数で指定した値を返します。

try_except decorator

これほんとネスト深くなって嫌ですねー。
何はともあれ、このデコレータを使えばこんな感じで、例外処理を簡単に使えるようになります。

decorate get_json

この @try_except をテストします。

テスト

デコレータは引数に関数を持つので、その関数をモックとして作ってあげなきゃいけません。
こういう時はunittestパッケージのMagicMockを使ってあげると楽に書けます。
また、戻り値や例外なんかも設定できます。

mock_func for test decorator

今回は、次の二つをテストします。

  • 例外が起きなかった場合にデコレートされた関数の戻り値が返ること
  • 例外が起きた時にデコレータに渡した引数が返ること

ここで注意なのですが、デコレータそのものをテストする際は、デコレータを直接関数として呼び出す必要があります。
引数付きデコレータの場合は関数が3つネストされているので、3回の関数呼び出しが必要です。
以上をまとめると、実際のテストコードはこんな感じになります。

test try_except_deco

無事テストできました。
とにかく大事なのはこの二つですね。

  • デコレートする関数をMagicMockを使ってモック化する
  • デコレータ自身を関数として呼び出す

参考

pythonのデコレータを使ってお手軽ベンチマーク - BlankTar
[Python] Exceptionをraiseするモック関数の作り方

歪んだ自分と向き合う話

ここ数日某社のインターンに行っていて、人と議論することが多い。
その場で自分がやっていることは今までしてきたこととだいたい近い気がする。
自分から新しく発言することはあまりなくて、人の発言を聞いて、自分なりに解釈して、ところどころでまとめようとする。 高専の頃は実行委員長として最初からまとめたり新しい発言をしていたけど、その前段階。

そのことをメンターに話した時、「本当に自分から発言しないことが最善なの?」とか「その役割がチームにとって最善なの?」と聞かれた。
たぶん今のあり方を否定するわけではなくて、「それが本当に正しいのか」「ほかの手段はないのか」ということを考えて欲しいんだと思う。

今日幡野広志さんの「ぼくが子どものころ、ほしかった親になる。」を読んで、人の在り方について感化された上、チームメンバーの日報を読んで「まずいことをしたな」とはっとさせられることがあったから、自分のことについて考えてみる。

  • 「なぜ、新しい発言をしようとしないのか」
  • 「自分が正しい」
  • 「自分が一番だ」
  • 「劣っているはずがない」
  • 「それが性格だから」で終わらせない
    • 2018年9月11日追記「完璧主義者」
続きを読む