twilioとSendGridとherokuでキャリアメール代替プッシュ通知


Motivation

au携帯をpovoにしてキャリアメールが使えなくなり、長女の公文終了時に来るメールをキャリアメールに転送してプッシュ通知してたのが、通常メールだと気付かず迎えを待たせてしまう。

gmailのフィルタで重要にタグ付けすれば、iPhoneでプッシュ通知できるはずなのだが、どう頑張っても私のiPhoneは通知してくれないので、gmailからメール転送してtwilioからsmsを送るようにした忘備録
こんなことせずとも海外キャリアだとemailからsmsに転送してくれるんだけど。。。
yahooメールとかに転送すればいいのですが。留守電は自宅の固定電話に転送するようにした。

やったこと

基本は下記twilioサイトのとおりなのだが、github上にあるソースコードがpython2用のものなので、直し直し動かした。
Send And Receive SMS Messages via Email with Twilio and SendGrid

ドメイン取得

gmailからのメール転送をトリガにSendGridのInbound Parse Webhookでheroku上のweb apiを叩くためにメールを受けるドメインとる必要があり、下記サイトを参考にfreenomというところでタダでドメイン取ろうとしたが、全然登録できないので、あきらめてAWSのRoute 53で年間9ドルでドメイン取得、DNSもRoute 53を使った。時間を無駄にしたので、やっぱりタダより高いものは無い。
SendGrid 新人成長記 第六回 Domain Authentication: メールの到達率を高めるために

DNS登録

Route 53でドメイン取得したら、上記サイトを参考にsendgridのSettings/Sender Authenticationにドメインを登録

SendGridのホストを新しいドメインのホストで引けるようにDNS(Route 53)に指示されるがままCNAMEを設定、新ドメイン宛のメールもmx.sendgrid.netが受けるようMXレコードも設定

DNSの反映は10分以上かかった。

twilioで電話番号購入

twilioでSMS送信元となる電話番号を購入する。日本の番号ではSMS送れないようなのでUSの番号を買う。番号維持料は157円/月くらい。

買った電話番号をProgrammable Messaging/Messageing serviceに紐づける。

heroku上にWebアプリ構築

前記twilioのサイトを参考に

$git clone https://github.com/jpf/sms-via-email.git
$cd sms-via-email

virtual env環境作る

$python3 -m venv venv
$source venv/bin/activate

git初期化

(venv)$git init

herokuにログインして、Webサイトを作成。web browserにパスワード入力求められる。

(venv)$heroku login
(venv)$heroku create

Creating app... done, ⬢ xxxxxxx-xxxxx-xxxxx
https://xxxxxx-xxxxx-xxxxx.herokuapp.com/ | https://git.heroku.com/xxxxxxx-xxxxx-xxxxx.git

上記URL+/handle-emailを後述sendgridのSettings/Inbound Parseに登録する。

(venv)$heroku git:remote -a xxxxxxx-xxxxx-xxxxx

twilioのDashboardに出てくるACCOUNT SID/AUTH TOKENを環境変数として設定

(venv)$heroku config:set TWILIO_ACCOUNT_SID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
(venv)$heroku config:set TWILIO_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

前述の通りgit上のapp.pyはpython2で書かれている、twilioのlibraryが更新されているので、python3で動くように修正(下記diff参照)
app.pyの中で勝手にtwilioのweb api叩いてくれるので、メールをSMSに転送するだけなら前述以上のtwilioサイト上の設定はいらない。

3,4c3
< import configparser
< from urllib.parse import urlparse, parse_qs
---
> import ConfigParser
9c8
< from twilio.rest import Client
---
> from twilio.rest import TwilioRestClient
24c23
<     user_list = configparser.ConfigParser()
---
>     user_list = ConfigParser.ConfigParser()
34c33
< twilio_api = Client(konf.twilio_account_sid, konf.twilio_auth_token)
---
> twilio_api = TwilioRestClient()
93c92
<         except Exception as e:
---
>         except Exception, e:
108c107
<     except Exception as e:
---
>     except Exception, e:
155c154
<         print(str(address_book))
---
>         print str(address_book)
186c185
<     except InvalidInput as e:
---
>     except InvalidInput, e:
206,213d204
<         warn("envelope: "+str(envelope))
<         warn("request.form[to]: "+str(request.form['to']))
<         warn("request.form[from]: "+str(request.form['from']))
<         warn("request.form[text]: "+str(lines[0]))
<         if envelope['to'][0] != request.form['to']:
<             email_from = '[email protected]'
<         else:
<             email_from = envelope['from']
215,216c206,207
<             'to': email_to_phone(envelope['to'][0]),
<             'from_': lookup.phone_for_email(email_from),
---
>             'to': email_to_phone(request.form['to']),
>             'from_': lookup.phone_for_email(envelope['from']),
219c210
<     except InvalidInput as e:
---
>     except InvalidInput, e:
223d213
<         warn("sms: "+str(sms))
227,228c217,218
<         print("oh no")
<         print(str(e))
---
>         print "oh no"
>         print str(e)
237c227
<         print("in debug mode")
---
>         print "in debug mode"

206~216行目あたりは、転送かけると、ヘッダとエンベロープのfrom/toが異なるので、違った場合は転送元(zzzzzzz@gmail)に紐付けた番号からsms送るように修正

requirement.txtも同様に修正

10c10
< phonenumbers
---
> phonenumbers==6.0.0
15,16c15,17
< twilio
< unittest2
---
> twilio==3.6.6
> unittest2==0.5.1
> wsgiref==0.1.2

address-book.cfgを作成

[users]
+8180xxxxxxxx: [email protected]
+1571wwwwwww: [email protected]

herokuのgitにpush

(venv)$git status
(venv)$git add -A
(venv)$git commit -m "hoge"
(venv)$git remote show heroku
(venv)$git push heroku master

SendGridのInbound Parse Webhook設定

SendGrid 新人成長記 第七回 Inbound Parse Webhookを用いてメールを受信する
上記サイトを参考にSendGridのSettings/Inbound Parseで新ドメイン宛のメールを受信したら前述heroku上のweb api https://xxxxxx-xxxxx-xxxxx.herokuapp.com/handle-email を叩くように設定する。

メール転送

gmail([email protected])から[email protected]にメールを送ったときのheroku logs抜粋、ちゃんとtwilio上で購入した+1571wwwwwwwからSMSきた。

2021-04-10T06:55:57.204402+00:00 app[web.1]: WARNING:root:['テスト\r\n']
2021-04-10T06:55:57.216212+00:00 app[web.1]: WARNING:root:{'to': '+8180xxxxxxxx', 'from_': '+1571wwwwwww', 'body': 'テスト\r\n'}
2021-04-10T06:55:57.920606+00:00 heroku[router]: at=info method=POST path="/handle-email" host=xxxxxxx-xxxxx-xxxxx.herokuapp.com request_id=ee5d9529-4778-40aa-9e35-71ea0269b839 fwd="167.89.119.77" dyno=web.1 connect=1ms service=1186ms status=200 bytes=192 protocol=https

その後、Google Voiceの番号を持ってたので、そちら宛に送るように設定した。Google VoiceはiPhoneでちゃんとプッシュ通知してくれる。。。

固定費 約250300円/月(番号維持費、ドメイン月割り、DNSホスト)、DNS queryとSMSの従量課金がどのくらいになるか、また後日

USの番号宛だとSMS1円/通、日本宛だと8円/通
https://www.twilio.com/sms/pricing/us
https://www.twilio.com/sms/pricing/jp

DNSクエリはメール100万通?くらい送らないと課金発生しなさそう?