UnityからSlackのメール転送機能にメールを送る

# Unityでメールを送りたい
色々ときっかけはあるのですが、省略してやりたいことを言います。
Slackをいろいろとプログラムで弄りたいときに、大抵は皆さんBOTを準備されると思うのですが、BOTを入れる権限がないSlackチャンネルでもいちおうできることがあります。それが、Slackのメール転送機能です。
Slackのチャンネル全体の設定から 環境設定 >> メッセージ＆メディア >> Slack へのメールの転送 と進むと、件の機能にたどり着くことができます。

> どんな重要メールも、固有のユニークメールアドレスに転送することで Slack にアップロードすることができます。メールは Slackbot がお届けし、非公開のままにしておくことも、またはチームと共有することもできます。

と、短く説明書きがあり、設定をすることで「固有のユニークメールアドレス」を発行することができます。
要するに、こいつにメール送るとSlackでも通知来るよって機能なのですが、こいつにUnityからメール送ってSlackに転送させたかった。
それがやりたいことの概要になります。
...なぜUnityかとか、聞かないでください。
プログラマーたるもの、自分のフィールドで戦いたいこともあるんじゃないですかね...笑

# 既存のUnityメール送信実装案の検討
## UniMail
まあ、有名なやつで、UniMailってやつがあります。

https://github.com/kyubuns/UniMail

UniMailの内部の処理は以外と簡単で、

```UniMail.cs
Application.OpenURL(string.Format("mailto:{0}?subject={1}&body={2}", mailTo, Uri.EscapeDataString(subject), Uri.EscapeDataString(body)));
```

この一文に尽きるって感じかなぁ。エディタでやるとなると特に。
自分の読解力がなくて見落としているかもですが。
結論を言うと、これ、自分の環境ではURLが開くだけでメール送信はできませんでした。

## Gmailで送信（コガネブログ様）
こちらの記事も調べている中で出てきました。

https://baba-s.hatenablog.com/entry/2017/11/08/090000

これは普通にいけそう。
でも当時の自分は、何をトチ狂っていたのか、勉強したてのUnityWebRequestを使いたいと思ってまして、これを却下してしまいました。
そこにこだわったせいで、慣れない別言語で苦しむことになるのですが...。

# GASを使うのだ
GAS（GoogleAppsScript）には便利なAPIがあることを、少し前から自分は知っていました。

```javascript
GmailApp.sendEmail('メールアドレス', '件名', '本文');
```

そうだ、これを使えばUnityでもメール送信できるじゃん！とか思って、そこから苦難の道が始まるんすよね...orz

- いちおうスプレッドシートにログを残して、とか考えたせいで、スプレッドシートからGASにたどり着くまでに苦労するし...
（以前使っていたときはメニューバーのツールからいけたんですけど、いつのまにか拡張機能に移動していた）
- Chromeでログインしているアカウントとは違うGoogleアカウントのスプレでやろうとしたから、そもそもGAS開けられないし...
（GASはChromeでログインしているアカウントと同じアカウントじゃないとGASにたどり着けないらしい）
- GASは変更があったら毎度毎度「新しいデプロイ」にしなきゃいけないことに詰まるし...

という、環境面で散々でした。
もちろん言語面でも苦労はあったんだけど、なんとか切り抜けました...。

# 実際の実装
## GAS（GoogleAppsScript）側の実装
```javascript:code.gs
function doPost(e)
{
  //  パラメータでJsonを受け取り、パース
  const jsonString = e.postData.getDataAsString(); //文字列で取得
  const date = JSON.parse(jsonString); //Object形式に変換
  const toMail = date.toMail; //メアド
  const subject = date.subject; //件名
  const body = date.body; //本文
  const timerecord = Utilities.formatDate(new Date(), "Asia/Tokyo", "yyyy/MM/dd HH:mm:ss"); //時刻
  const writeDate = [[timerecord, date.toMail, date.subject, date.body]];
  
  //スプレッドシートにログを残す
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getActiveSheet();
  const maxRow = sheet.getMaxRows();
  const lastRow = sheet.getRange(maxRow, 1).getNextDataCell(SpreadsheetApp.Direction.UP).getRow();
  sheet.getRange(lastRow+1, 1, 1, 4).setValues(writeDate);

  //メール送信
  GmailApp.sendEmail(toMail, date.subject, date.body);

  let output = ContentService.createTextOutput();
  output.setMimeType(ContentService.MimeType.JSON);
  output.setContent(JSON.stringify({ message: "success" }));
  return output;
}
```

## Unity側の実装
実際はUniTask使っていたんですけど、こちらではコルーチン版貼りますね。
いちおう挙動は確認済です。

```c#:SlackMail.cs
using System;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;

public class SlackMail : MonoBehaviour
{
    // 必要なデータ
    [SerializeField] private MailData mailData = null;
    
    // GASの公開リンク
    [SerializeField] private string URI = "";
    
    private static readonly (string name, string value) header = ("Content-Type", "application/json");
    
    [System.Serializable]
    public sealed class MailData
    {
        public string toMail; //メールアドレス
        public string subject; //件名
        public string body; //本文
    }

    [System.Serializable]
    public sealed class ResponseData
    {
        public string message;
    }
    
    private void Start()
    {
        StartCoroutine(Post(mailData));
    }

    private IEnumerator Post(MailData postMailData)
    {
        if (mailData == null)
        {
            yield break;
        }
        
        var json = JsonUtility.ToJson(postMailData);
        var bodyRaw = Encoding.UTF8.GetBytes(json);

        var request = new UnityWebRequest(URI, UnityWebRequest.kHttpVerbPOST)
        {
            uploadHandler = new UploadHandlerRaw(bodyRaw),
            downloadHandler = new DownloadHandlerBuffer()
        };

        request.SetRequestHeader(header.name, header.value);

        yield return request.SendWebRequest();

        // 非同期処理の結果を参照する
        if (request.result != UnityWebRequest.Result.Success)
        {
            // 通信失敗時
            Debug.LogException(new Exception(request.error));
            yield break;
        }
        
        var result = request.downloadHandler.text;
        Debug.Log(result);
        var response = JsonUtility.FromJson<ResponseData>(result);
        Debug.Log(response.message);
    }
}
```

あとは、必要事項をUnityエディタから登録してもらって...

# あとがき
なんだかんだ言って、GASがわかってきた気がするので、これ使って色々しようかなと。
UnityのNetcodeと連携とか、純粋にGASのみで開発もありっすね。
まあ、疲れましたが...

# 参考文献
https://baba-s.hatenablog.com/entry/2017/11/01/090000

https://baba-s.hatenablog.com/entry/2020/11/16/080000_9

https://www.hanachiru-blog.com/entry/2020/04/07/120000

これは書籍だけど、貼っておくか...参考になったので...

https://www.borndigital.co.jp/book/22432.html