simフリー版ドコモメールの着信をIMAP4 IDLE→IFTTT→PushBullet でプッシュ通知

ドコモ回線で SIMフリー端末(Android)へ変更すると、ドコモメール(spモードメール)の着信が特殊SMSで通知されなくなる
simフリー端末用の公式ドコモメールアプリは、15分間隔でのポーリング受信
連絡は他のツールに移りつつあるため、あまり支障はないが、実験的メモです

注意
キャリア端末での着信通知は SMSで起動するため、Android端末が Doze 状態でも通知されますが、今回の方法は Androidのプッシュを最終的に利用しているため、Dozeの影響を受けます
着信トリガーで、SMSや電話着信を起動すれば、リアルタイム性を保つことは可能かと思います(今回はそこまで突っ込んでません)

試したけどNGだった事
  • ドコモメール設定(spモード接続)から user-agentを iPhone へ変更して、着信お知らせメール(SMS)をオンにしてみる → 通常SMSが到着した形跡なし
  • ドコモ公式端末から DcmWapPushHelper.apk と jp.nttdocomo.carriermail-2.apk を移植 → jp.nttdocomo.carriermail-2.apk(ドコモメール)は、起動と強制終了を繰り返して利用できず、DcmWapPushHelper.apk + ドコモメール(simフリー用)は、プッシュの反応なし
ということで、IMAP4 の IDLEを用いて、メール着信イベントを取得するしかないという事になりました

IMAP4 IDLE は早い話、セッションをつないだままにしてボックスに変化があったらサーバからクライアントへメッセージを送る --- という方法なため、端末アプリで IMAP4 IDLE を用いると、常にセッションを張り続ける→電池を消耗する という事は容易に想像されます。
今回は、常時起動したサーバ(今回は手持ちのVPS)で、IMAP4 IDLEを監視して、メール受信イベント →  IFTTT WebHook → PushBullet で端末に「何か来たよ」の通知を行う事としました
メール受信イベント以下は、Gmailへ通知を送るなどでも同じです

※ imap.select('inbox') で受信フォルダを選択しています
それ以外のフォルダの通知を受ける場合、他のを指定する必要があります
日本語の場合 utf7でエンコード?(未確認)

Pyhotnソース(終了は ctrl + d) 同じ場所に log ディレクトリを作成してください
  1. import datetime  
  2. import imaplib  
  3. import httplib2  
  4. import urllib  
  5. import time  
  6. import signal  
  7. import os  
  8.   
  9. signal.signal(signal.SIGINT, signal.SIG_DFL) # ctrl + d  
  10.   
  11. # imap4へ接続して imapオブジェクトを返す(失敗は False)  
  12. def connect_iamp_server():  
  13.     imap_server = 'imap.spmode.ne.jp'  
  14.     imap_port = '993'  
  15.     imap_user = '******@docomo.ne.jp'  
  16.     imap_pass = '******'  
  17.   
  18.     try:  
  19.         imap = imaplib.IMAP4_SSL(imap_server,imap_port)  
  20.         sock = imap.socket()  
  21.         # sock.settimeout(60 * 15) # NATタイマーにかかる場合タイムアウトを設定  
  22.         imap.login(imap_user,imap_pass)  
  23.     except imaplib.IMAP4_SSL.error as e:  
  24.         print(e)  
  25.         return False  
  26.     else:  
  27.         return imap  
  28.   
  29.   
  30. # Http GET or POST  
  31. def httpExecute(url, user = '', passwd = '', postData = ''):  
  32.     for key in postData:  
  33.         if postData[key] == None:  
  34.             postData[key] = ''  
  35.   
  36.     http = httplib2.Http(timeout=8)  
  37.     if user != '':  
  38.         http.add_credentials(user, passwd)  
  39.   
  40.     if postData != '':  
  41.         try:  
  42.             content = http.request(url,  
  43.                             method="POST",  
  44.                             headers={'Content-type''application/x-www-form-urlencoded'},  
  45.                             body=urllib.parse.urlencode(postData) )[1]  
  46.             return content.decode()  
  47.         except httplib2.ServerNotFoundError:  
  48.             return ''  
  49.     else:  
  50.         try:  
  51.             (resp, content) = http.request(url, 'GET')  
  52.             return content.decode()  
  53.         except httplib2.ServerNotFoundError:  
  54.             return ''  
  55.   
  56.   
  57. def printtime(s):  
  58.     now = datetime.datetime.now()  
  59.     writeLn = now.strftime("%Y-%m-%d %H:%M:%S ") + s  
  60.     print(writeLn)  
  61.   
  62.     logFile = os.path.dirname(os.path.abspath(__file__)) + os.sep + "log" + os.sep + now.strftime("%Y%m%d") + ".log"  
  63.     with open(logFile, mode='a') as f:  
  64.         f.write(writeLn + '\n')  
  65.   
  66.   
  67. def main():  
  68.     while True:  
  69.         mailExists = False  
  70.         imap = connect_iamp_server()  
  71.         if imap != False:  
  72.             printtime('M: Connected IMAP4 Host')  
  73.             imap.select('inbox')  
  74.             imap_idletag = imap._new_tag()  
  75.             printtime('C: %s IDLE'%(imap_idletag.decode('utf-8')))  
  76.             imap.send(b'%s IDLE\r\n'%(imap_idletag))  
  77.   
  78.             imap_line = imap.readline().strip().decode('utf-8')  
  79.             printtime('S: ' + imap_line)  
  80.             if imap_line == '+ .':  
  81.                 try:  
  82.                     while True:  
  83.                             imap_line = imap.readline().strip().decode('utf-8')  
  84.                             printtime('S: ' + imap_line)  
  85.                             if imap_line.startswith('* BYE 'or (len(imap_line) == 0):  
  86.                                 mailExists = False  
  87.                                 break  
  88.                             if imap_line.endswith('EXISTS'):  
  89.                                 mailExists = True  
  90.                                 printtime('C: DONE')  
  91.                                 imap.send(b'DONE\r\n')  
  92.                                 imap_line = imap.readline().strip().decode('utf-8')  
  93.                                 printtime('S: ' + imap_line)  
  94.                                 break  
  95.                             # 上記以外のコマンドはループ(IMAP4を操作してもレスポンスが返ってくる)  
  96.                 except:  
  97.                      mailExists = False  
  98.                      pass  
  99.                 finally:  
  100.                     # imap.close() # タイムアウト時にエラーが発生  
  101.                     imap.logout()  
  102.                     printtime('M: Disconnect IMAP4 Host')  
  103.   
  104.             if mailExists:  
  105.                 printtime('M: mail Exists')  
  106.                 # メール到着処理  
  107.                 postData = {'value1''ドコモメール'}  
  108.                 httpresponse = httpExecute('https://maker.ifttt.com/trigger/*******/with/key/*******''''', postData)  
  109.                 printtime('M: IFTTT > ' + httpresponse)  
  110.         else:  
  111.             # 接続NG -> 60秒待ち  
  112.             time.sleep(60)  
  113.   
  114.   
  115. if __name__ == '__main__':  
  116.     main()  
  117.     

ドコモの仕様書どおり IDLE 30分で BYE切断になるが、NATタイマーがそれ以下の場合、タイムアウトを設定する(コメントアウトを外す)

コマンドラインでうまくいったら、pythonをデーモン化するメモ あたりでデーモンとして監視させればOK


参考文献
imap4libでpush通知と遊んでみる --- デーモン移行時、改行コードに注意

コメント