夏休み自由工作:通販したら着弾日がTimeTreeに登録されるやつ③
ハロートナミです。
自分の作業と平行してブログを書いているので更新頻度が高いです。
さて、引き続き
Amazonから届く確認メールをあれこれして、TimeTreeに着弾時間を登録するやつ
を作っていきます。
これは続きなので、前のやつを読みたい方はこれとこれを読んで下さい。
前回の記事でAmazonからの発送通知メールそれがしする処理を書き
TimeTreeの予定投稿APIを使って予定登録するところまでやりました。
なので、残りのやることが
- Lambdaに乗せる
- 動かしてみる
という感じになっていました。もう少しで終わりですね。それではやっていきましょう。
Lambdaに乗せる
AWS Lambdaにデプロイします。まずは準備から。
準備
AWS Lambdaは標準ライブラリ以外を使う際、それ用の準備をしないとimport errorで落ちます。
で、今回のアプリはrequestsという外部ライブラリを利用しています。
requests.readthedocs.io
urllibとか使えば標準ライブラリだけで書ききる事も出来たんですが
元々Lambdaでrequestsを使うための仕組みを持っており、流用出来るので楽をしたという感じです。
dev.classmethod.jp
みんな大好きclassmethod様のブログにそのものズバリが書いてあります。
classmethod様のブログ以上の内容が書ける気がしないので内容は省略します。
classmethod様のブログを読んで下さい。
はい、これでAWS Lambdaでrequestsが使えるようになりました。
コード修正
AWS Lambdaでpythonコードを動かす時はlambda_function.pyというファイルを用意し
lambda_handlerという関数を指定の内容で定義しておく必要があります。
前回最後に載せた、TimeTreeAPI連携とAmazonのメール取得を連携させるコードをこんな感じにします。
import datetime import MailServer import TimeTree import os def lambda_handler(event, context): server = os.environ['MAIL_SERVER'] user = os.environ['MAIL_SERVER_USER'] password = os.environ['MAIL_SERVER_PASS'] tt = TimeTree.TimeTree() tt.setup_calendar() ms = MailServer.MailServer(server) ms.login(user, password) estimates = ms.get_amazon_arriving_estimates() for estimate_date, description in estimates: yaer = datetime.datetime.now().year if datetime.datetime.now().month == 12 and '01/' in estimate_date: # 12月中に届いた1月のお届予定は来年1月として処理 yaer += 1 estimate_date = datetime.datetime(year=yaer, month=int(estimate_date[0:2]), day=int(estimate_date[3:5])) tt.add_schedule('amazon来る', description, estimate_date) ms.logout()
関数名が変わったぐらいです。
これをlambda_function.pyという名前で保存しておきます。
①と②の記事で書いたコードもそれぞれ名前を付けて保存し、作成したファイルが
- TimeTree.py (TimeTreeにAPIを発行し、予定を登録するやつ)
- MailServer.py (amazonの発送通知メールを探し、内容を抽出するやつ)
- lambda_function.py (lambda上で動作し、Main関数的に上2つの内容を組み合わせて動くやつ)
の3つになりました。
デプロイ
はじめに、今作った3つのファイルをzipファイルで固めておきます。
AWSコンソールにログインし、サービスからLambdaを選択しLambdaの管理ページへ移動します。
ダッシュボードの関数の作成ボタンとかから関数の作成を開始します。こんな画面になると思います。
一から作成を選択し、関数名には好きな名前をつけます。
私のローカル環境のpythonが3.7.4だったので、ランタイムは3.7を指定。これで関数を作成します。
関数の編集画面になると思うんですが、そしたら
関数コード右上のアクション>.zipファイルをアップロードから、作成したzipをアップロードします。
あと、Layersからレイヤーの追加を選択し、準備編で触れたrequestsを使うためのレイヤを追加します。
レイヤとソースコードの準備が出来たらテストしてみましょう。
ページ上部のテストボタンを押すとこんなのが出ます。
contextに渡される内容をjson形式で編集出来るのですが、今回は引数不要なので何でもいいです。
このまま保存し、テストを実行してみます。
上の方にテスト結果が出ます。失敗した場合、こんな感じで
詳細部分を開くと、エラーメッセージなどがに書いてあります。
動かなければ原因を調べてなおす、を繰り返して動くようにします。
Lambda関数が動くようになったら、最後にトリガーを追加します。
今回は30分に1回ぐらい動いてくれればいいかなと思うので
Cloud Watch eventsにcron式を設定し、毎時0分と30分で動くようにしました。
こんな感じです。作成したトリガーをLambda関数に登録し、デプロイ完了です。
本当は監視がどうとか色々やった方が良い要素があるんですが、とりあえず動きます。
動いてくれ頼む。
動かすために適当にamazonで買い物してみましょう。
動作確認
色々買った後なので欲しい物が思いつかず、適当なおもちゃを買おうと思います。
適当なおもちゃです。買ってみましょう。
順当にamazonから発送通知メールが届き、その後
……何も起きませんでした。
この記事、正常に動く前提で後書きまで下書きしてたんですが、動きませんでした……。
何が起きたのか検証しましたが、いくつか問題が分かりました。
- amazonのメール、今まで英語で来てたのに、設定確認したら日本語になった。
- メールに商品名等が記載されるのはamazon発送以外の商品を買った時だけだった。
- yahooのメールサーバ、2段階認証の期限が切れるとuser/passだけだとログイン出来ない。
えーめんどくさ。1個ずつ詳細書きます。
amazonのメールが日本語になった
何か使えるネタ無いかなーとamazonのメール設定周りを確認したんですが
設定画面に入った事がきっかけ?で言語設定が変わってしまいました。なんで?
これによってメールが日本語で来るようになったので、メール検索部分と
気合パース部分が使えなくなりました。
一応日本語でも動くか確認したんですけど、IMAP-UTF7とかいうおファックな文字コードが出てきて
心のお嬢様がクソわよになってしまったので考えたくないです。
そもそもメールに商品名が書いてない事が多い
amazonの発送通知メールに商品名が記載されるのは、amazon発送の商品以外を買った時のみです。
コード書いてる時に参考にしたメールには商品名が書いてあったんですが
それらはたまたま、amazon以外発送の商品を買っていたからでした……。
という訳で、メールから商品名を取得する手段は使えないケースが多い事が分かりました。
yahooのメールサーバ、2段階認証の期限が切れるとログイン出来ない。
これはちょっと正しいか分からないんですが、検証中急に認証で弾かれるようになりました。
BANされた?とか考えて少し調べたのですがそれらしい情報は当たらず
セキュリティのためにログインが制限されてる?なら本家側にも何かあるかな?と考え
Yahooメールのサイトからログインしてみると、SMSを利用した2段階認証が走りました。
で、2段階認証を通した後、Lambda関数側でもログイン出来るようになりました。
どう管理されてるか知らないのですが、2段階認証の有効期限があって、それが切れると
2段階認証を通す手順を踏まない限り永遠に関数がコケる。みたいな感じでしょうか……うーむ。
問題が分かったところで今後の展開ですが
- 仕組みは大きく変えず、メールからamazonが届く事だけをTimeTreeに登録する。
- もうメールは諦めて別の手段にいく。
のどっちかです。心底面倒くさいですが、少し考えてみます。
事前の調査は大事。思いついたからっていきなり実装するとこういう事になります。
注意してやっていきましょう。おわり