調べて、学ぶ

検索したり実験したことを覚えとくブログ

googleAPIを駆使するLINEbotサービスを作った #2

前回の続き。

LINEbotの問いかけの返事はpostbackで取得できることがわかった。

なので、これからはメインの処理に入る

1.gMailに届くメールを取得して文章を分解する

2.分解した内容をgoogleカレンダーに登録する

3.分解した内容をLINEbotのユーザ(お友達登録した人)にブロードキャスト

4.取得したgMailを「処理済み」トレイに移動

1.gMailに届くメールを取得して文章を分解する

googleAPIのことは知っていたので、「node gmail」でググるとほぼコピペで済む。

今回利用したのはこちら。

Google公式ライブラリを利用してNode.jsからGmailの送受信をしてみよう

…って、あれ、この方は#1のときにbotの手ほどきを書いてくれていた方だ。二度もお世話になっているとは…感謝しまくりですわ。

ここでハマったのが本家のチュートリアル(https://developers.google.com/gmail/api/quickstart/nodejs)で、この内容をソースに貼り付けてメール取得&取得内容の変数を分解しようとすると、うまく行くのに、この内容を別ファイルにして保存・呼び出しするとなぜだか変数がundefinedになっている。

//うまくいかない処理(イメージ)
const mailUtil = require([外部ファイルにしたメール取得処理])

const maillist = mailUtil.getMailList(); //gmailのメール一覧を取得
console.log(maillist.getMailText(maillist[0].id))  //前の処理で得られたメールのIDを元にテキスト取得

//maillist[0].idはなぜかundefinedになる

しばらく「なんで?」と考えた結果気づいたのは、node.jsの1番の特徴「ノンブロッキングIO」。時間のかかる処理(上述のgetMailList()の部分)が終わる前に、次の処理へ行くことを最優先する”非同期”と言われる仕組み。こいつのおかげで、gMailへアクセスしている処理が返って来る前にメイン処理は次へ次へと進んでいた。1番の特徴でありながら、そいつに足をすくわれていた(--;

さて…このサンプルをどうしたら「メールを取得後に処理」できるのか。

 1.ウェイトを挟む

  => ウェイトしてまだ処理済みになってなかったら意味がない  

 2.イベント化してメールを取得したらイベント発火

  => わりと現実味あり。

  調べてみると「イベントエミッター(EventEmitter)」てのがあるみたいだけど、書かれている日付が軒並み「古い」。なんかもっといい方法がある気がする…

 3.”非同期”を”同期”する

 =>これが欲しい。「node 非同期 同期」で調べるとasync/await 、promise()の話が出て来るのでしばらく読んだ結果、「できることは同じ。async/awaitはpromiseの糖衣構文(簡単にしたもの)」とのこと。どうせなら源流に近いほうをやっておきたいので、promise()を使用したら…おぉ、できた。

//別ファイルにした処理(mailUtil.js)
//(要点抜粋)
//◆◆ メールIDの一覧を取得 ◆◆
exports.getMailList=function (){
  return new Promise((resolve)=>{
    resolve(authorize(JSON.parse(credDat))) //チュートリアルにある認証処理の終了待ち(終了したら認証情報が返る)
  }).then((res)=>{   //取得した認証情報を変数resとして利用
    const oauth = res;

    return new Promise((resolve)=>{
      //自身のメールボックスから「受信トレイにある未読メール」を一覧取得
      gmail.users.messages.list({userId:'me',auth:oauth,'labelIds':LabelID_remove},(err,res)=>{
        if(err) return console.error('Error getting message list', err);
        resolve(res.data.messages)
      })
    })
  })
}

exports.getMaiilText = function(pMailid){
  return new Promise((resolve)=>{
    resolve(authorize(JSON.parse(credDat)))
  }).then((res)=>{
    const oauth = res

    return new Promise((resolve)=>{
      gmail.users.messages.get({userId:'me',id:pMailid,auth:oauth},(err,res)=>{
        if(err) console.log('The API returned an error: ' + err)
        
        let ret={'Subject':'','Text':''};
        ret.Subject = res.data.payload.headers.filter(function(elm){return elm.name=='Subject'})[0].value;

        if(res.data.payload.parts[0].body.size == 0){
          ret.Text = 'The mail size 0 byte';
          resolve(ret)

        }else{
          //受け取ったメールテキストはbase64でエンコードされているので、デコードする。
          const b64str = Buffer.from(res.data.payload.parts[0].body.data,'base64');
          ret.Text = b64str.toString('utf-8'); 
          //console.log(res)
          resolve(ret)
        }
      })
    })
  })
}
//前述の処理の改善
const mailUtil = require(./mailUtil.js)

mailUtil.getMailList().then((list)=>{
     //受信トレイのメールIDを取得
     mailUtil.getMailText(list.id).then((mailtext)=>{   
       console.log(mailtext);
     }
})

これで「IDをもとにメールを読む」ができる。

なかなか厄介。

#3へ続く