乃木坂46メッセージをLINEbotで作る （乃木坂モバメ）

# 一言で
Gmailに届いたメールをLINEに自動で転送し、LINEでメールが確認できるようにします。

# はじめに
乃木坂46は今年でデビューから9年目。過去に作られたホームページや、今はCMもない755など、古いシステムを今でも使っています。特にモバイルメール（モバメ）は顕著で、LINEなどのメッセージアプリが発達した今でもメール＋画像添付を基本としており、一方的に送りつけるだけで双方的なコミュニケーションは取れません。一方で日向坂46など比較的新しいグループはメッセージアプリを運用しており、早く乃木坂46もメッセージアプリ移行して欲しいと多くのオタクが思っています。しかし、乃木坂46の運営には一向にその気配がありません。そこで今回は自力で乃木坂46メッセージを実装したいと思います。（といってもできることはモバメをLINEで閲覧できるようにするだけです）


# 準備するもの

- LINEアカウント
- Googleアカウント
- Gmailのメールアドレス（モバメを受信している前提）

# 手順
 1. LINE bot のアカウントを準備
 2. 転送用のスクリプトを準備
 3. 手動実行してみる
 4. 定期実行を設定する


## 1. LINE botのアカウントを準備する
今回はLINEのMessaging APIを利用したbotを使うので、開発者用のアカウントに登録します。
[ここ](https://developers.line.biz/ja/docs/messaging-api/getting-started/#_1-line-developersコンソールにログインする)を参考に普段使っているLINEアカウントでログイン・登録してください。登録できたら、手順に従ってプロバイダー・チャンネルを作成します。
- チャンネル:Messaging API
- プロバイダー名:なんでも（自分の名前 or 乃木坂46など）。
- チャンネル名:LINEに表示されるもの。（モバメ・乃木坂メッセージなど）後で変更可能
- アイコン:推しメンの顔のアップなど（小さいのでヨリの写真がいいです）
- 説明：適当
- 業種：適当
続いて、後ほど使用するアクセストークンを発行します。
[ここ](https://developers.line.biz/console/)から先程作ったチャンネルを選択。
"Messaging API設定"タブを選択し、下の方にある”チャンネルアクセストークン”の”発行”をクリックします。生成された文字列は後で使うのでメモしておいてください。
また、"チャンネル基本設定"の下の方にある”あなたのユーザーID”もメモしておいてください。

ここまででLINE側の準備は終了です。

## 2.モバメをLINEに転送するスクリプトを書く
今回はGoogleAppScriptを使ってGmailに届いたメールをLINEに転送します。
1. [ここ](https://script.google.com/home)にアクセス
2. 新しいプロジェクトをクリック
3. 下記のコードを”コード.gs”の中に全部コピペ（元あったコードは消していいです。ファイル名は変えてもいい）
4. 1行目の`access_token`の部分に先程メモしたアクセストークンをコピペ
5. 2行目の`userID`の部分を先程メモしたユーサーIDをコピペ
6. `member_name`のメールアドレスの部分を自分が購読しているメンバーのものに書き換え（基本は下の名前_苗字）対応するメンバーの名前の部分もあだ名などに変更
7. `member_image`のメールアドレスを書き換え対応する部分に各メンバーの顔写真などの画像URLに書き換える。画像のURLには必ず`https`から始まるものを使ってください。

6.7についてはメンバーが多いor少ない場合、適宜削除・追加してください。

```javascript
// LINE Developersに書いてあるChannel Access Token
var access_token = "abcdefgh";
var userID = "userID";


var member_name = {"nogizaka46-tamami_sakaguchi@m.nogizaka46.com": "たまちゃん",
                   "nogizaka46-minami_umezawa@m.nogizaka46.com": "うめちゃん",
                    "nogizaka46-shiori_kubo@m.nogizaka46.com" : "久保ちゃん",
                    "nogizaka46-yuuki_yoda@m.nogizaka46.com": "よだちゃん",
                   "nogizaka46-kazumi_takayama@m.nogizaka46.com" : "かずみん"}

var member_image = {"nogizaka46-tamami_sakaguchi@m.nogizaka46.com": "画像URL1",
                   "nogizaka46-minami_umezawa@m.nogizaka46.com": "画像URL2",
                    "nogizaka46-shiori_kubo@m.nogizaka46.com" : "画像URL3",
                    "nogizaka46-yuuki_yoda@m.nogizaka46.com": "画像URL4",
                   "nogizaka46-kazumi_takayama@m.nogizaka46.com" : "画像URL5"}

function sendLine() {
  sendAll(get_allmail(), userID);
}

function push(message, to) {
  var url = "https://api.line.me/v2/bot/message/push";
  var headers = {
    "Content-Type" : "application/json; charset=UTF-8",
    'Authorization': 'Bearer ' + access_token,
  };
  var postData = {
    "to" : to,
    "messages" : message 
  };

  var options = {
    "method" : "post",
    "headers" : headers,
    "payload" : JSON.stringify(postData)
  };

  return UrlFetchApp.fetch(url, options);
}


function get_allmail() {
  const interval = 5;
  const date = new Date() ;//現在時刻を取得
  const unixTime = date.getTime();//UNIX時間に変換
  const now = Math.floor(unixTime/1000); //ミリ秒を秒に変換
  const term = now - 60 * interval; //現在時刻から5分前
  const termStr = term.toString(); //検索期間を文字列に変換
  //乃木坂のモバメのドメインを持つアドレス＋未読のものを検索対象にします
  const condition = "from:@m.nogizaka46.com" + " after:" + term + " is:unread";
  var thds = GmailApp.search(condition, 0, 20);
  var messe = GmailApp.getMessagesForThreads(thds);
  var res = [];
  for(var i = 0; i < messe.length; i++) {
      for(var j = 0; j < messe[i].length; j++){
        var target = messe[i][j];
        var body = target.getBody();
        var myRegexp = /img src="([\s\S]*?)">/g;
        var email_body = target.getPlainBody();
        var from = target.getFrom();
        //既読にする
        target.markRead();
        var mail_date = Moment.moment(target.getDate());
        var now_date = Moment.moment();
        var diff = now_date.diff(mail_date, 'minutes');
        //スレッド対策
        if (diff > interval) {
           Logger.log('以前のものはスキップ');
           continue;
        }
        
        var title = messe[i][j].getSubject();
        urls = body.match(myRegexp);
        var mail_res = {"title": title, "body": email_body, "image": [], "from" : from };
        //画像が複数枚あるので
        if(urls) {
            var tmp_arr = [];
            for(var k = 0; k < urls.length; k++ ) {
               url = urls[k].slice(9,-2);
               url = url.replace('http', 'https');
               tmp_arr.push(url)
            }
            mail_res["image"] = tmp_arr;
        }
        res.push(mail_res);
      }
  }
  return res;
}
function sendAll(mobame, to){
   var messages = [];
    for (var i=0; i < mobame.length; i++) {
      text = mobame[i]["title"] + "\n" + mobame[i]["body"];
      text = convert_emoji(text);
      mail = mobame[i]["from"].slice(1,-1);
      message_text = {
        "type": "text",
        "text": text,
        'sender': {
          'name': member_name[mail],
          'iconUrl': member_image[mail]
        }
      };
      messages.push(message_text);
      if (mobame[i]["image"]) {
        urls = mobame[i]["image"];
        for(var j = 0; j < urls.length; j++ ){
          message_image = {'type':'image',
                           'originalContentUrl':urls[j],
                           'previewImageUrl':urls[j],
                           'sender': {
                            'name': member_name[mail],
                            'iconUrl': member_image[mail]
                            }
                           };
          messages.push(message_image);
        }
      }
    }
    //対象がなければ終了
  if (messages.length == 0){
    return;
  }
  if (messages.length <= 5) {
    //5件以下ならそのまま送信
    push(messages, to);
  }else{
    var max = messages.length;
    var start = 0;
    var end = 5;
    //5件以上ある場合5件ずつにわけて複数回送る
    while(end <= max){
       push(messages.slice(start,end), to);
       start += 5;
       end += 5;
    }
    push(messages.slice(start), to);
  }
}
//絵文字を変換する
function convert_emoji(text) {
   var reg = /&#\d+;/g;
   var emoji = text.match(reg);
   if (emoji) {
    for (var i=0; i < emoji.length; i++) {
       var reg = /\d+/;
       var decimal = Number(emoji[i].match(reg))
       Logger.log(decimal);
       var unic = String.fromCodePoint(decimal);
       
       text = text.replace(emoji[i], unic)
    }
  }
  return text
}


```

## 3.手動実行してみる
続いて、スクリプトの手動実行をしてみます。その前に、[こちら](https://tonari-it.com/gas-moment-js-moment/)を参考に2のライブラリを追加する作業を行ってください。これがないと実行時にエラーがでます。
ライブラリの設定が完了したら、`実行→関数を実行→sendLine`から手動実行します。初回時には確認画面が出るので以下の手順に従って進めてください。
1. "許可を確認"をクリック
2. 自分のアカウントを選択
3. 「このアプリは確認されていません」→詳細をクリック
4. "安全ではないページに移動"をクリック
5. "許可"をクリック
このタイミングでスクリプトが実行されます。モバメの検索対象は5分以内のものですので、その時点で5分以内にメールが来ていて、未読の場合LINEに自動転送されます。5分以内に未読のモバメがない場合は`  const interval = 5;`この部分を60などにすると60分前までを対象とできます。あくまでもテストなので確認したい場合のみで大丈夫です。変更した場合は必ず元に戻しましょう。


## 4. 定期実行のトリガーを設定する
ここまでで、ボタンを押せばモバメをLINEに転送することができるようになりました。これを自動で実行できるようにします。今回は5分に一回自動で実行するようにします。
スクリプトのページから"編集→現在のプロジェクトのトリガー"をクリック。移動した先でトリガーを追加をクリック。下の画像のように設定します。
<img width="631" alt="Screen Shot 2020-08-12 at 10.22.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60236/583439b4-5024-df9a-3539-3898da005542.png">

せっかちな方で1分ごとに送って欲しい場合は1分おきに設定してください。ただし、スクリプトの数字`const interval = 5`を`1`に変更するのを忘れないように。


### 現時点での問題
- 一部のメンバーにおいて、送られてくるLINEに転送される画像が高画質でない場合があります。（齋藤飛鳥・高山一実など）これはメンバーが使っている画像の送信方式に違いがあることが原因なのですが、対象のメンバーが少ないことや、3期生・4期生などの新しいメンバーは使用していないこともあり、今後を考えてそのままにしています。これらのメンバーについてはメール本文をチェックして高画質画像を閲覧してください。
- 一月に1000件までの上限があります。スクリプト上で色々と工夫していますが、10名以上のメールを受信している場合1000件を超える可能性は高いと思います。月に1000件を超えた場合はそれ以上は転送されなくなってしまうので注意してください。

# まとめ
今回は、乃木坂46のモバメをLINEに自動転送するLINEbotを実装しました。忙しくてメールは確認しにくくてもLINEなら見れるという時はあると思います。また、LINEに送るメリットとして画像の一覧が見やすくなります。過去に送られた画像の一覧をタイル上に並べて閲覧したり、少しスクロールすれば直近のメールが確認できてとても便利です。少し手間がかかりますが一度設定しておけば今後のオタ活が捗ること間違いなしです。ぜひやってみてください！

### 参考文献
https://qiita.com/3mc/items/39b2c8241c6b52811ad2
https://qiita.com/mick_k1218/items/0ceb411f63a298decad2