【Google Apps Script, Slack】彼女にバレずにワクワクメールのメッセージに気付くために夜な夜な怪しげなスクリプトを組んだ話

# まえがき

僕には彼女がいる。
でも、世の中には他にたくさんの女の子がいる。
彼女は彼女で大事、ただ、他のたくさんの女の子も大事。
そう、僕はゲス野郎。その自覚はある。

そんなある日、意気揚々とワクワクメールを始めてしまった。
もちろん Web 版で。
僕の彼女は、僕の携帯をたまにチェックする。
だから、変なアプリが入っているとマズい。
僕はブラウザのシークレットモードでのみ、ワクワクメールの Web 版を楽しむ。
ログイン情報は僕の頭の中。
これで何もかも完璧。そう思っていた。

ワクワクメールでは、たくさんの女の子とメッセージをすることができる。
さあ、いろんな女の子とメッセージしよう！
そう意気込んで、女の子にメッセージを送りまくる僕。
数時間たって、ブラウザでワクワクメールをチェック！
すると、何件か返事が返ってきているじゃないか。

なんだ、もっと早くチェックすればよかった。そう思った。
そしてさらに思った。
ワクワクメールのメッセージが来た時に、携帯にポップアップが出れば、
僕はいち早く女の子からのメッセージに気付くことができる。
そう、僕はとんでもないゲス野郎。その自覚はある。

しかし、自分のスマホにワクワクメールのアプリを入れて、
「あすかさんからメッセージが届きました！」（名前は適当）
なんてポップアップが彼女の前で出てきたら・・・。
僕の命が灯火が消えてしまうことは想像に難くない。

じゃあ、彼女にバレないようなポップアップだったら良いんじゃね？

幸い僕は IT エンジニア。
欲しい機能がないなら、自分で作ればいいじゃない。

謎の原動力に背中を押され、僕は夜な夜なパソコンに向かい、怪しげに作業を開始したのであった。

# 実現したい事

次項のシステム概要図も合わせてご参照ください。

- ワクワクメールから、メッセージを受け取った旨のメール通知が Gmail に飛ぶ
- そのメールからユーザ名を抽出し、ユーザ名を隠蔽した自分だけに分かる文字列に変換する
- ユーザ名を隠蔽した文字列を、通知オンとなっている Slack のチャンネル（#tsuchi）に飛ばし、スマホのポップアップとして出るようにする
- 上記のメールからメッセージ本文も抽出し、通知オフとなっている Slack のチャンネル（#hitsuchi）に飛ばす
- 通知がオフなので、ポップアップには出ないが、上記通知オンのポップアップに気付いた際に、彼女から見えないところでメッセージ本文をチェックできる。(ワクワクメールの Web 版にログインするのが面倒なため)

# システム概要図

まず、僕はシステム概要図を書いてみることにした。
![スクリプト概要図.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395375/902c55be-399d-ddf2-38dd-e460331e92bb.jpeg)


- まず、ワクワクメールから以下のようなメール通知が、登録してある GMail のアドレスに飛ぶ

| 要素 | 内容 |
|:------|:------|
| メールタイトル  | [ワクワク]メッセージが届きました  |
| メール本文  | ♀あすかさんからのメールです<br><br>イケメンさんこんにちは！<br>私は、都心でOLをしてます^^<br><br>イケメンさんって、ほんとに顔がかっこいいですね！<br><br><br>サイトにアクセスして返信↓(5P/5S)<br>http://hensin.shimashou.example.com/henshin/shimashou?saa=hayaku<br><br>...<br>(つづく) |
- 次に、Google Apps Script にて 1 分おきに動くスクリプトを用意する
  - スクリプトでは、まず Gmail の内容をチェックし、該当のタイトルのメールを抽出する
  - 抽出したメールの中で、スター（★）がついていないメールにおいて、メール本文から、ユーザ情報およびメッセージ本文を抽出する
  - ユーザ情報を隠蔽した文字列を、Slack に用意した"通知あり"のチャンネル（#tsuchi）に飛ばす
  - ユーザ情報およびメッセージ本文を、Slack に用意した"通知なし"のチャンネル（#hitsuchi）に飛ばす
  - もちろん、処理が終わったメールについては、スター（★）をつけることを忘れない


# 前提事項

- Slack にて、"通知あり"のチャンネル（#tsuchi）および"通知なし"のチャンネル（#hitsuchi）は作成済み
  - 上記チャンネルに対応する Webhook URL は、Slack にログインした状態で、以下の URL にアクセスすることで設定済み
     - https://slack.com/services/new/incoming-webhook 
- 彼女は Slack を知らない
  - Slack の動作を細かく知っていると、自分の意図しないところまでアプリの中を見られてしまう可能性があるため
- 彼女は IT 系ではない
  - ごまかしたポップアップメッセージを見られた際、"このメッセージが何か"を技術的に追及されてしまうと危険なため
- 彼女は英語ペラペラではない
  - 英語の文章の中に、ユーザ名を隠蔽した文字列を入れたいので、英語がめちゃめちゃ出来てしまうと、メッセージの違和感に気付く可能性があるため 
- 僕は初心者エンジニア
  - ソースコードが微妙な場合は、優しく教えてくれると嬉しいです(笑) 

# 実装したスクリプト

僕は、以下のようなソースコードを書いた。

```javascript:コード.gs
// Title : Slack 通知スクリプト
// Qiita ： 83k

// 検索したい件名を入れる
var SUBJECT_WAKUWAKU = '[ワクワク]メッセージが届きました';

// POST 送信先の URL を指定
// #tsuchi チャンネル用の URL を指定
var SEND_URL_USERNAMEONLY = 'https://hooks.slack.com/services/ABCDE1234/MNOPQ5678/XXXXXXXXXXXXXXXXXXXXXX';
// #hitsuchi チャンネル用の URL を指定
var SEND_URL_ALLELEMENT = "https://hooks.slack.com/services/ABCDE1234/MNOPQ5678/XXXXXXXXXXXXXXXXXXXXXX";

// ワクワクメールの処理 
function CheckMessageAndCallSendText_Wakuwaku() {
  // 指定した件名のスレッドを検索して取得
  var myThreads = GmailApp.search(SUBJECT_WAKUWAKU, 0, 10); 
  // スレッドからメールを取得し二次元配列に格納
  var myMessages = GmailApp.getMessagesForThreads(myThreads);
  
  // 二次元配列の処理
  for(var i in myMessages){
    for(var j in myMessages[i]){
      // Star がないメッセージのみ処理する
      if(!myMessages[i][j].isStarred()){
        
        // メールタイトルを抜き出す
        // 本プログラム中では利用しないが、今後利用するときのために書き記しておく
        var strSubject = myMessages[i][j].getSubject();
        // メール本文において、位置 0 から 1000 文字抜き出す
        var strMessage = myMessages[i][j].getPlainBody().slice(0,1000);
        
        // メッセージ送信者の名前を格納
        var fromUserName = strMessage.substring(strMessage.indexOf("♀") + 1 ,strMessage.indexOf("さんからのメールです"));
        // メッセージ送信者の名前とメッセージ本文を格納
        var userNameAndMessage = strMessage.substring(0, strMessage.indexOf("サイトにアクセスして返信"));
        
        // ユーザ名を元に、隠ぺいして slack 送信するための文字列を生成
        var tranUserName = "Nothing";
        if (fromUserName == "あすか"){
          tranUserName = "Ask";
        }else if (fromUserName == "ちひろ"){
          tranUserName = "Chr";
        }else if (fromUserName == "メアリー"){
          tranUserName = "Mar";
        }else{
          tranUserName = "Others";
        }
        
        var usernameText = "A very important " + tranUserName + " event is coming today. Check the content and take appropriate action.";
        
        // slack 送信メソッドを呼び出す
        SendInfo(usernameText, SEND_URL_USERNAMEONLY);
        
        // 処理済みのメッセージに Star をつける
        // この段階で Star をつけることにより、メール本文にバックスラッシュが含まれてしまうことによるエラーで、該当メールに Star が付かなくなることを防ぐ
        myMessages[i][j].star();
        
        // slack 送信メソッドを呼び出す
        SendInfo(userNameAndMessage, SEND_URL_ALLELEMENT);
  
      }
    }
  }
}

function SendInfo(text, url){
  // 送信する payload の作成
  var payload = '{"text" : "' + text + '"}';
  
  // payload を POST 送信するよう定義する
  var options =
  {
    "method" : "post",
    "payload" : payload
  };
  
  // 指定した URL に POST 送信を行う
   UrlFetchApp.fetch(url, options);
}

```

ソースコードを読み慣れているプロの方々からしたら、一瞬で読めてしまうようなコードではあるが、初心者の僕の復習も兼ね、上からざっくりとさらっていくことにする。

まずは、メールタイトルと、通知オンおよび通知オフのチャンネルの Slack Webhook URL を記述した。
var じゃなくて、const にしたかったけど、なぜか実行時にエラーになってしまった。
なんでだろう。
でも、とりあえず動けばいいので、エラー回避のために var にしておいた。（ソースコードの神様ごめんなさい）
(constはトランスパイルしないと使えないとの情報をコメントでいただきました！ありがとうございます！)

```javascript:
// 検索したい件名を入れる
var SUBJECT_WAKUWAKU = '[ワクワク]メッセージが届きました';

// POST 送信先の URL を指定
// #tsuchi チャンネル用の URL を指定
var SEND_URL_USERNAMEONLY = 'https://hooks.slack.com/services/ABCDE1234/MNOPQ5678/XXXXXXXXXXXXXXXXXXXXXX';
// #hitsuchi チャンネル用の URL を指定
var SEND_URL_ALLELEMENT = "https://hooks.slack.com/services/ABCDE1234/MNOPQ5678/XXXXXXXXXXXXXXXXXXXXXX";
```

下記の箇所で、該当のタイトルを含むメールを取得する形とした。

```javascript:
  // 指定した件名のスレッドを検索して取得
  var myThreads = GmailApp.search(SUBJECT_WAKUWAKU, 0, 10); 
  // スレッドからメールを取得し二次元配列に格納
  var myMessages = GmailApp.getMessagesForThreads(myThreads);
```

以下の箇所で、メールタイトル、メッセージ送信者（女の子）の名前、メッセージ本文を取得する形とした。
もっとスマートなやり方があるのかもしれないが、一刻も早く女の子からのメールを自動処理したい僕に立ち止まっている余裕はない。

```javascript:
        // メールタイトルを抜き出す
        var strSubject = myMessages[i][j].getSubject();
        // メール本文において、位置 0 から 1000 文字抜き出す
        var strMessage = myMessages[i][j].getPlainBody().slice(0,1000);

        // メッセージ送信者の名前を格納
        var fromUserName = strMessage.substring(strMessage.indexOf("♀") + 1 ,strMessage.indexOf("さんからのメールです"));
        // メッセージ送信者の名前とメッセージ本文を格納
        var userNameAndMessage = strMessage.substring(0, strMessage.indexOf("サイトにアクセスして返信"));
```

以下の箇所で、ユーザ名を隠蔽する。
英文の中に隠蔽するので、英語に変換してみよう。
運悪く彼女が僕のスマホのポップアップを見てしまっても、まさか "Ask" が "あすか" だとは思うまい。

```javascript:
        // ユーザ名を元に、隠ぺいして slack 送信するための文字列を生成
        var tranUserName = "Nothing";
        if (fromUserName == "あすか"){
          tranUserName = "Ask";
        }else if (fromUserName == "ちひろ"){
          tranUserName = "Chr";
        }else if (fromUserName == "メアリー"){
          tranUserName = "Mar";
        }else{
          tranUserName = "Others";
        }
```

そして、本システムのキモとなる箇所。
ここで、ユーザ名を隠蔽した文字列を、英文の中に埋め込む。

とりあえず、通知的なニュアンスがある適当な日本語を Google 翻訳で英語にしてみよう

> 本日は非常に大事なイベントが控えております。内容についてチェックして、適切な行動をしてください
>
> A very important event is coming today. Check the content and take appropriate action.

ここにしれっとユーザ名を埋め込んでみよう。

> A very important (ユーザ名を隠蔽した文字列) event is coming today. Check the content and take appropriate action.

一見すると英文のように見えるけど、僕がチェックするのは、important の後ろの単語だけなんだ。
ソースコードはこんな感じ。死ぬほど簡単。隠蔽文字列入れるだけ。

```javascript:
        var usernameText = "A very important " + tranUserName + " event is coming today. Check the content and take appropriate action.";
```

さあ、ここまでできたら、あとは Slack に情報を投げるだけ。
送信メソッドの内容は後述するが、ユーザ名を隠蔽した文字列を Slack の 通知オンチャンネル（#tsuchi）に送った後、該当メールにスター（★）を付ける。
その後、ユーザ名およびメッセージ本文が入った文字列を Slack の 通知オフチャンネル（#hitsuchi）に送る。

なぜ、この順番かというと、
僕の使用したテストデータの 1つにメッセージ本文にバックスラッシュ(\\)が入っているものがあり、メール本文を Slack に送る処理にてエラーになってしまったからだ。
エラーになってしまうと、そこでスクリプトが強制終了してしまい、該当メールにスター（★）が付かなくなってしまうので、メッセージ本文を Slack に送る処理の前にスター（★）を付けようと思ったんだ。

ユーザ名にバックスラッシュ(\\)が入っていたらどうするかって？そんな女の子はこっちから願い下げなんだ。
でも、エスケープ処理をしないことで、イケメン（自称）の僕に見捨てられる悲運な女の子が出てきてしまうかもしれないのか・・・。
そんな思いが一瞬僕の頭を駆け巡ったが、一刻も早く女の子からのメールを自動処理したい僕に立ち止まっている余裕はない。（←面倒くさがり、ソースコードの神様ごめんなさい）


```javascript:
        // slack 送信メソッドを呼び出す
        SendInfo(usernameText, SEND_URL_USERNAMEONLY);

        // 処理済みのメッセージに Star をつける
        // この段階で Star をつけることにより、メール本文にバックスラッシュが含まれてしまうことによるエラーで、該当メールに Star が付かなくなることを防ぐ
        myMessages[i][j].star();

        // slack 送信メソッドを呼び出す
        SendInfo(userNameAndMessage, SEND_URL_ALLELEMENT);
```

そして、Slack 送信のメソッドの箇所。
送信する POST データを作成し、指定した URL に投げるだけ。
SendInfo メソッドさん、女の子からのメッセージを彼女にバレないように、僕にそっと耳打ちしておくれ。

```javascript:
function SendInfo(text, url){
  // 送信する payload の作成
  var payload = '{"text" : "' + text + '"}';

  // payload を POST 送信するよう定義する
  var options =
  {
    "method" : "post",
    "payload" : payload
  };

  // 指定した URL に POST 送信を行う
   UrlFetchApp.fetch(url, options);
}
```


そして、ソースコードの神様に謝りまくって作り上げたこのスクリプトを動かすために、Google の設定も忘れずに行わなくては。
スクリプト編集画面にて、「編集」⇒「現在のプロジェクトのトリガー」を選択。
![トリガー1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395375/3b44686a-2244-847e-4ad3-77058df72edc.jpeg)
画面右下部の「トリガーを追加」を選択。
![トリガー2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395375/1822e71e-c9a0-2cff-d229-945dde42ad2c.jpeg)
下記のように、CheckMessageAndCallSendText_Wakuwaku メソッドを 1分おきに実行するように設定しよう。
これで気になる女の子からのメールも1分以内にすぐ気づけるぞ。
![トリガー3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395375/1d58ee65-a630-9d6e-a33b-4414c6fe8ad8.jpeg)

こうして一通り設定が完了した。
女の子のメッセージにすぐに気づきたいがために、夜な夜な作業に没頭してしまう自分に若干引きつつ、
僕は眠りについた。

# 動作確認

そして次の日、意気揚々と女の子にメッセージを送る僕。
さあ来い、女の子からのメッセージ！！！
今の僕ならいつでも君の声に耳を傾けられる。さあ、来るんだ！

しばらく時間が経ったであろうか。
僕の相棒 iPhone にポップアップが現れる。
キターーーーーーーーーーー！！！！！
<div align="center"><img width="500" alt="popup" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395375/a683f662-fd29-a1d3-29e1-afe181b5a26a.png"></div>

設定どおりの内容になっている。
そしてしっかり女の子の名前も隠蔽されている。
これは完璧すぐる。

次に、肝心の非通知チャンネルに向けたメッセージを見てみよう。
<div align="center"><img width="500" alt="message" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395375/c9a24cfd-b994-c4c3-318f-cffdba6cbcc7.png"></div>

メッセージ本文も問題なく見れているでなはいか。
これまた完璧すぐる。
そして、やっぱり僕の顔ってカッコいいんだなー。あすかちゃん見る目あるなー。（棒読み & 自作自演）

こうして無事にスクリプトは動き、僕はほっと胸を撫で下ろすのであった。


# あとがき

その後、僕は彼女と良好な関係を維持しつつも、
他の女の子からのメッセージにいち早く気づけるようになり、マメな男アピールを武器にますます多くの女の子とメッセージをやり取りするのであった。
そんな僕のスマホに、今日も女の子からのメッセージが Slack のポップアップで通知される。
隣には彼女がいる。でも、僕は慌てない。
スマートに、あたかも仕事関係の Slack メッセージを読み流す感じでポップアップを閉じる。
こんなことが出来ちゃうんだ。そう、Slack と Google Apps Script ならね。


本記事の内容は、以上となります。
ここまでお読みいただいてありがとうございました！

# 参考にさせていただきましたサイト様

https://asatte.biz/gmail-line/
https://qiita.com/n0bisuke/items/a31a99232e50461eb00f