Node.jsで社内日報メールを取得してFacebookページに自動投稿する仕組みを実装したので解説します。

のびすけ


Node.jsで社内日報メールを取得してFacebookページに自動投稿する仕組みを実装したので解説します。

こんにちは、エンジニアののびすけです。今度ジーズアカデミーという学校でメンターを務めることになりました。

さて、うちのチームリーダーのウラカワさんのびすけをテーマにしたネタを最近投稿しているのですが、最近では社内の日報やFacebookページと投稿先を拡大し始めました。チームリーダーでただでさえ忙しいのに色々と投稿するのは大変だと思い、作業を自動化した話をしたいと思います。
Node.jsで実装したので、この辺のことを調べているエンジニアの参考になれば幸いです。

きっかけ

今回の取り組みのきっかけです。

LIGの日報

大前提としてLIGの日報システムを紹介します。LIGでは退勤時に社内システムから今日の日報を書いて提出するルールがあります。これに「今日の一言/思ったこと」 を書く項目があり、この部分に社員がツラツラと適当なことを書いていたりします。

こんな感じのフォーマットです。

 

このシステムで日報を提出すると、同時に社内メールで社員全員に通知が来る仕組みになっています。

 

日報提出忘れが多い

日報は毎日書くのが普通なのですが、人間なのでどうしても忘れてしまうとうことがあります。最近では出し忘れをする人が増えてしまい、ついに社長から全社に対して 「日報をちゃんと出しましょう」とお怒りの言葉が・・・
社長たちは 「今日の一言/思ったこと」 の項目を楽しみにしているようです。

日報をちゃんと書く -> 短編小説になった

うちのチームリーダーのウラカワさん日報をちゃんと出すために、日報に小説を書き始めました。

a2ee8958cbd4835735246883b57958f6

勢いでFacebookページまで作り出す

勢いとは書きましたが、きっと 面白い取り組みは外部にも発信したいと思ったのだと僕は感じています。

ちなみにこちらがそのFacebookページです。

 

確かにチェックしがいがあります(笑)

チームリーダーの負担を軽減してあげたいと思い自動化

日報とFacebookに投稿するのは二度手間です。チームリーダーのウラカワさんは毎日とても忙しく働いています。

その上、小説四コマを書いているのでお風呂に入る時間もありません。

チームメンバーの一人として、少しでもリーダーの負担軽減ができればと思い、今回の仕組みを作ってみました。

システム全体構成の紹介

日報メールが配信されたあとの処理手順を説明したいと思います。

  • システム用にGmailアカウントを新規作成
  • 日報メールを新規作成したGmailに転送する
    • (A)新規作成したGmailアカウントにNode.jsからアクセスする
    • (B)FacebookAPIにNode.jsからアクセスする
    • (C) 仕上げに(A)と(B)をつなぎ合わせる
  • おまけ:社内チャットツールにも通知する(やり方は特に紹介しません)

という流れになります。

 

ちなみに、日報のシステムAPIは存在しないのでメールから取得する実装にしています。この記事を読んだ社内システム担当者のてっさんがいつか対応してくれることを祈っています(笑)

実装の流れ

今回やりたいことは大きく分けると、Node.jsでメールを受信することとNode.jsでFacebookページに自動投稿することの二つの要素の組み合わせです。どちらか片方だけの情報を探している方もいるかもしれないので、二つの要素はそれぞれ独立した形で紹介したいと思います。

  • A. Node.jsでメール受信する仕組みを作る
  • B. Node.jsでFacebookページに自動投稿する仕組みを作る
  • C. AとBを繋げる

開発環境

筆者の開発環境を紹介します。箇条書きのinbox,iconv,fbは全てNode.jsのモジュールです。
Node.jsの環境を用意できていない方は、過去記事「いまアツいJavaScript!ゼロから始めるNode.js入門〜5分で環境構築編〜」を参考に環境構築を5分でしてみましょう!

  • Mac OS X 10.10 Yosemite
  • Node.js v0.12.1(最近だとiojsを使うのもありですね!)
  • inbox v1.1.59
  • iconv v2.1.6
  • fb v0.7.0

A. Node.jsでメール受信

メールの設定

まずはじめに、システム用にGmailアカウントを作成しましょう。 その後、会社のメールの設定と新規作成したGmailの設定をします。
会社のメールアドレスでそのままシステムに繋げると思わぬ情報が外部に漏れてしまう可能性もあるので、会社のメール -> 今回作成したGmailアカウントという形で、一度フィルタリングをするようにしています。

フィルタ&転送設定(会社のメールアドレス)

社内システムから送信されれる日報メールは、会社のメールアドレス宛で、【日報】という件名が入っています。

なので、この件名を条件にしてフィルタリングを設定します。LIGではGoogle Appsを使っているのでGmailの設定項目で説明します。

Gmailの場合は、設定 -> フィルタから設定できます。

 

IMAPの設定(システム用に新規作成したGmailアカウント)

IMAPの設定を有効にしておくことで、外部からメールにアクセスすることができるようになります。

 

設定 -> メール転送とPOP/IMAPから設定出来ます。

アプリのアクセス設定 (システム用に新規作成したGmailアカウント)

Googleアカウントのアカウント設定からログイン項目内の安全性の低いアプリのアクセスをオン(許可)にしましょう。

 

これを許可しておかないとNode.jsからアクセス時にエラーになると思います。

簡単な実装

まずは、メール受信だけを実装してみます。
必要モジュールをインストールしましょう。

$ npm install inbox

inboxはGmailへのアクセスを簡単にできるようにしてくれるモジュールです。
それではmail.jsを作成してみましょう。6,7行目は新規作成したGmailアカウントのユーザー名とパスワードです。

var inbox = require('inbox');

/*Gmailへの接続情報をセット*/
var client = inbox.createConnection(false, "imap.gmail.com", {
  secureConnection: true,
  auth: {
    user: "Your Gmail Account",
    pass: "Password"
  }
});

/*Gmailに接続成功時に呼ばれる*/
client.on("connect", function() {
  client.openMailbox("INBOX", function(error, info) {
    if(error) throw error;
    console.log("Successfully connected to server");
  });
});

/*新規メールが届いたときに呼ばれる*/
client.on("new", function(message) {
  console.log('日時:' + message.date);
  console.log('送信者:' + message.from.name + '-' + message.from.address);
  console.log('タイトル:' + message.title);
});

/*Gmailへの接続を試みる*/
client.connect();

これだけの記述でNode.jsからGmailアクセスができてしまいます! とても簡単ですね。

試してみます。

実際に試してみましょう。

$ node mail.js
Successfully connected to server

問題がなければSuccessfully connected to serverと表示されるはずです。

この状態でNode.jsを待機させておき、先ほど作ったアカウント宛にメールを送ります。

 

アカウントがメールを受信すると、そのタイミングでNode.jsのコンソールに表示されます。

 

これでメール受信ができました!

メール本文をパースしてみる

この状態だとまだ、メールの本文の内容をもとに処理をすることができません。
iconvモジュールをインストールして、メール本文の情報を解析しましょう。

$ npm install iconv

client.createMessageStream()の処理を追加します。32行目のbody変数iconvで文字コード変換したメール本文が入ってきます。

日報のフォーマットをおさらいします。【今日の一言/思ったこと】【今日の仕事内容】の間の寝落ちしてました。。が今日の一言になりますね。

 
ヨシキさん日報

これはヨシキさんの日報です。ということで、メールの 本文から今日の一言部分だけを抜き出す処理を追記します。

var inbox = require('inbox');
var iconv = require('iconv'); //追加
var conv = new iconv.Iconv("ISO-2022-JP", "UTF-8"); //追加

var client = inbox.createConnection(false, "imap.gmail.com", {
  secureConnection: true,
  auth: {
    user: "Your Gmail Account",
    pass: "Password"
  }
});

client.on("connect", function() {
  client.openMailbox("INBOX", function(error, info) {
    if(error) throw error;
    console.log("Successfully connected to server");
  });
});

client.on("new", function(message) {
  var tmp = []; //追加
  var phrase = '【今日の一言/思ったこと】'; //追加
  var endPhrase = '【今日の仕事内容】'; //追加

  console.log('日時:' + message.date);
  console.log('送信者:' + message.from.name + '-' + message.from.address);
  console.log('タイトル:' + message.title);

  /*追加*/
  client.createMessageStream(message.UID).on("data", function(data){
    var body = conv.convert(data).toString();

    if(body.indexOf(phrase) != -1) {
      tmp = body.split(phrase);
      tmp = tmp[1].split(endPhrase);
      console.log('今日の一言:', tmp[0]);
    }
  });

});

client.connect();

実行してみましょう。

 

実行してしばらく待ち、みんなが日報を登録して帰るころに、こんな感じで、みんなの今日の一言が表示されました。

これにて、Node.jsでメールを受け取る仕組みが完成です。

B. Node.jsでFacebookページに自動投稿

次にFacebookページへの自動投稿を解説してみます。
※FacebookのAPIは時期によって仕様が変わったり管理画面のUIが変わったりするので、あくまで執筆時点(2015年3月)の内容になります。適宜読み替えが必要になる場合もあると思います。

facebookページの準備

自動投稿をするFacebookページを作成するか、誰かが作ったページの管理権限を貰いましょう。

くどいようですが、今回はこちらのページの管理権限を貰いました。

 

Facebookアプリの準備

Facebookアプリ開発者ページからアプリを新規作成します。

 

My Apps -> Add a New Appを選択しましょう

 

作成するアプリ名を入力してCreate New Facebook App IDを選択しましょう。今回はnobiserverという名前のFacebookアプリを作成しました。

 

カテゴリを選択してCreate App IDを選択します。なんとなく、 今回の小説はエンターテイメントだと思ったのでエンターテイメントのカテゴリに設定しました。

 

チュートリアル画面になりますが、左上のSkip Quick Startを選択しましょう。

 

こんな感じで今作成したFacebookアプリの管理画面が表示されます。

 

ここの画面のApp IDとApp Secretをメモしておきましょう。後ほど利用します。

Showを押したときにパスワードを求められますが、 自分がFacebookにログインするときのパスワードです。
 

Facebookページのアクセストークンを取得する

ここまでの手順でFacebookアプリとFacebookページを作成することができました。次に、Facebookページへのアクセストークン(自動的に投稿させるときなどに利用する鍵のようなもの)を取得してみます。
Graph API Explorerにアクセスしましょう。

 

上部のApplicationのプルダウンメニューで先ほど作成したFacebookアプリの名前を選択します。

 

Get Access Tokenを選択します。モーダルが表示されるので、Extended Permissionsのタブを選択し、manage_pagesにチェックを入れて、Get Access Tokenを押します。これで自分が管理しているFacebookページへFacebookアプリから投稿することが可能になります。

 

ポップアップウィンドウが表示されます。OKを選択して進めましょう。

 

 

すると、先ほどまで空だったAccess Tokenに値が入ります。

 

Graph APIを選択した(デフォルト)状態で、フォームにme/accounts/と入力してSubmitボタンを押します。

 

すると、以下のようなJSONデータが出力されます。
このデータは自分が管理権限があるFacebookページの数だけ出力されます。

nameの値を見て、自動投稿したいFacebookページのaccess_tokenの値をメモしましょう。例えば、Backend 〜サーバーと一体化した男の話〜というFacebookページaccess_tokenの場合は以下のxxx…で伏字になっている部分となります。

{
      "access_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "category": "Blogger",
      "name": "Backend 〜サーバーと一体化した男の話〜",
      "id": "753405358113275",
      "perms": [
        "ADMINISTER",
        "EDIT_PROFILE",
        "CREATE_CONTENT",
        "MODERATE_CONTENT",
        "CREATE_ADS",
        "BASIC_ADMIN"
      ]
    }

これでFacebookページへ自動投稿をする情報をゲットできました。

[補足]アクセストークンの有効期限を極力長く設定する

Facebookページへの自動投稿をする場合を考えると、ずっとそのアクセストークンを使いたいところです。しかし、Facebookの仕様上(おそらくセキュリティ的な都合で)ずっと同じアクセストークンを使うことはできません。
少し古い記事ですが、参考サイト(Facebookが無期限のoffline_access Tokenを廃止し有効期限が最大60日間に – 対応はお早めに!)によると、昔はできていたみたいですが、今は最大60日間になっているみたいです。
つまり60日後にはもう一度アクセストークンの取得をする必要があります。何もしないと数時間でアクセストークンの有効期限は切れてしまうので、今回のような場合は設定をしておいたほうがいいでしょう。

Removal of offline_access permissionのサイトによると、

https://graph.facebook.com/oauth/access_token?
    client_id=APP_ID&
    client_secret=APP_SECRET&
    grant_type=fb_exchange_token&
    fb_exchange_token=EXISTING_ACCESS_TOKEN

という感じのアクセスをすればOKらしいです。一度アクセスして有効期限を伸ばした状態のアクセストークンを取得してしまえば、あとは有効期限内ならずっと使えるようになります。

先ほどのFacebookアプリの管理画面でApp IDとApp Secretをメモしたと思います。

 

この情報と、先ほど取得したFacebookページのアクセストークンを利用します。

仮にそれぞれの値が以下のようになっている場合は、

  • App ID : AAAAAA
  • App Secret : BBBBBB
  • Facebookページのアクセストークン : CCCCCC
https://graph.facebook.com/oauth/access_token?
    client_id=AAAAAA&
    client_secret=BBBBBB&
    grant_type=fb_exchange_token&
    fb_exchange_token=CCCCCC

というURLにアクセスしましょう。各々の環境に合わせて値は読み替えてください。

 

expires=5184000と表示されてますが、この5184000はアクセストークンの有効期限の秒数になります。(5184000秒->60日)

このaccess_token=xxxxxxの部分が有効期限を伸ばした状態のアクセストークンになります。

大事に保管しましょう。

実装

前準備でだいぶ長い記述になってしまいましたが、FacebookページのアクセストークンがあればOKです。

それではfbモジュールをインストールして実装を進めていきます。

$ npm install fb

以下のようにfb.jsを作成しましょう。とりあえずサンプルを動かします。
FB.setAccessTokenには先ほど取得したFacebookページのアクセストークンを入力します。

var FB = require('fb');
FB.setAccessToken('access_token');

var body = 'My first post using facebook-node-sdk';
FB.api('me/feed', 'post', { message: body}, function (res) {
  if(!res || res.error) {
    console.log(!res ? 'error occurred' : res.error);
    return;
  }
  console.log('Post Id: ' + res.id);
});

実行してみましょう。

$ node fb.js
Post Id: 753405358113275_753684558085355

Facebookページを確認すると、’My first post using facebook-node-sdk’と投稿されていると思います。

 

C. 仕上げにAとBの内容をつなげる

最後は二つをつなげれば完成です。
先ほどのfb.jsをモジュール化してmail.jsから呼ぶようにします。

var FB = require('fb');
var FB_ACCESS_TOKEN = 'YOUR ACCESS TOKEN';

FB.setAccessToken(FB_ACCESS_TOKEN);

module.exports = function fbPost(body){
  console.log(111);
  FB.api('me/feed', 'post', { message: body}, function (res) {
    if(!res || res.error) {
      console.log(!res ? 'error occurred' : res.error);
      return;
    }
    console.log('Post Id: ' + res.id);
  });
}

通常の日報と判別するために【今日の短編小説】というキーワードで判別することにして、ウラカワさんには【今日の短編小説】というキーワードを日報小説の前に入れてもらうことにしました。

var inbox = require('inbox');
var iconv = require('iconv');
var fbPost = require('./fb'); //追記 mail.jsとfb.jsは同じ階層に設置

var conv = new iconv.Iconv("ISO-2022-JP", "UTF-8");

var client = inbox.createConnection(false, "imap.gmail.com", {
  secureConnection: true,
  auth: {
    user: "Your Gmail Account",
    pass: "Password"
  }
});

client.on("connect", function() {
  console.log('connect');
  client.openMailbox("INBOX", function(error, info) {
    if(error) throw error;
    console.log("Successfully connected to server");
  });
});

client.on("new", function(message) {
  var tmp = [];
  var phrase = '【今日の短編小説】';
  var endPhrase = '【今日の仕事内容】';

  console.log('日時:' + message.date);
  console.log('送信者:' + message.from.name + '-' + message.from.address);
  console.log('タイトル:' + message.title);

  client.createMessageStream(message.UID).on("data", function(data){
    var body = conv.convert(data).toString();
    if(body.indexOf(phrase) != -1) {
      tmp = body.split(phrase);
      tmp = tmp[1].split(endPhrase);
      console.log('今日の短編小説:', tmp[0]);
      fbPost(tmp[0]); //追記 facebookにポスト
    }
  });

});

client.connect();

これで完成です。

実行してみます。

$ node mail.js

実行したらあとは待機しておきましょう。

ウラカワさんがメールを流したら・・・

 


Facebookページに自動投稿されます!

めでたしめでたし。

利用者の声

うちのチームリーダーのウラカワさんに感想を聞いてみました。

 

「の、のびちゃん・・・そんなことより、その髪型・・・どうしたの?」

うん、よかったよかった。

まとめと感想など

今回は、浦川さんの日報小説をメールで受け取り、Facebookページに自動投稿する過程を紹介しました。

今回はウラカワさんが小説を書き始めたという勢いにのって、日報小説をメールで受け取り、Facebookページに自動投稿する過程システムを作り、記事公開までをやりました。おそらくウラカワさんもそのうち日報小説に飽きると思うので、思い立った時に勢いで作るというのはとても大事なことだなぁ感じてます。
刺身と同じで 制作物の鮮度ってあるよなと思ったりしながらこの締めの文章を書いています(笑)

今回は、LIGでこんな取り組みをしている・・・ということだったり、エンジニアにとって記事が参考になったら幸いです。
このタイトルを見てどんな人がここまで読んでくれるのか、制作過程の話やコンセプトを織り交ぜつつの技術系記事ってリーチするのか・・・など思うところはあります、ので。

コメントなどお待ちしております。

それでは!

 

【がんばる、のびすけ】

Seriously.jsで動画・画像にリアルタイムエフェクトをかける方法【ほりぼーいドッキリ編】

ChatWorkがメッセージ取得APIを解禁したのでHubotと連携させてみよう!(業界初かも)

SHIFTBRAIN×LIGのサンタたちが、働くオトナにワクワクをお届けしました!

milkcocoaとgmaps.jsで、スマホ(と、まろ氏)の位置情報をリアルタイムに取得してみた

iPhoneがラジコンになる知育ロボットRomoで遊んでみよう【使い方編】

のびすけ
この記事を書いた人
のびすけ

バックエンドエンジニア

おすすめ記事

Recommended by