Google Apps Script で Gmail Add-on を作ってみよう

先日 Tokyo GAS のイベントに行ってきた @wezardnet です。ちなみに東●ガス会社とはまったく関係ありませんｗ
久々に社外の勉強会に参加して来たのだけど、自社内（ウチだけかも知れないけど）に閉じこもってると、ホント世の中の技術から取り残されていくことを改めて痛感しました（ヤバイね）:fearful:

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">4 年ぶりの開催となるTokyo GAS on GCPUG 2018 Spring に来ましたょ♫ <a href="https://twitter.com/hashtag/tokyogas?src=hash&amp;ref_src=twsrc%5Etfw">#tokyogas</a> <a href="https://twitter.com/hashtag/gasja?src=hash&amp;ref_src=twsrc%5Etfw">#gasja</a> <a href="https://twitter.com/hashtag/gcpug?src=hash&amp;ref_src=twsrc%5Etfw">#gcpug</a> <a href="https://t.co/udx00GGT4x">pic.twitter.com/udx00GGT4x</a></p>&mdash; wezardnet (@wezardnet) <a href="https://twitter.com/wezardnet/status/981106039633330176?ref_src=twsrc%5Etfw">2018年4月3日</a></blockquote>


# 1. Gmail Add-on って？
サードパーティー製のアプリをアドオンとして Gmail に組み込みできる機能で 2017 年の 10 月ごろに発表されたのですが、日本ではあまり浸透しなかった感がありますｗｗ

Gmail アドオンでは Gmail のサイドバーを独自にカスタマイズして機能を追加することができます。また Android の Gmail アプリからも利用することができるので機能次第では重宝しそうです。尚 iOS の Gmail アプリは現在のところ未対応なので、今後のアップデートに期待したいですね！

* [Gmailがサードパーティー製アドオンに対応](https://jp.techcrunch.com/2017/10/25/20171024gmail-gets-third-party-add-ons/)
* [Google、Gmailのアドオン機能をリリース。同じアドオンをAndroidでも利用可能](https://japanese.engadget.com/2017/10/25/google-gmail-android/)
* [ビジネスで注目したいGmailに新たに用意された「アドオン」　その仕組みとChrome「拡張機能」との違いとは？](http://news.livedoor.com/article/detail/13927201/)
* [Gmailの新機能アドオンを試してみた話。どんな人向けの機能があるの？追加、削除方法、使い方など。](https://www.pizaman.com/entry/gmail-add-on-201710/)

>2018/04/09　追記
タグフィードを見ていたら、一足先に試された方が Qiita に記事をアップしてましたので、こちらも参考にすると良いです。日本ではまだまだ情報が少ないです、、、

>* [Gmail Add-On やってみた](https://qiita.com/yamazako/items/d43b00557a4b77a162c1)



# 2. 作ったモノ
Tokyo GAS で Gmail アドオンを Google Apps Script(GAS) で作れるってことを知り、たまたま社内に相談案件があって、アドオンの仕組みがマッチしそうだったので試してみることにしました。

表示中のメール本文と、（あれば）添付ファイルを Google ドライブに格納するアドオンを作ってみました♪
↓な感じで動きます。

![sample.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/12023/df455ffa-164f-4d86-5235-ddc7a9b381d8.gif)

ドライブ エクスポートを実行すると、↓のように Google ドライブの「Gmail Export」というフォルダに PDF 化したメール本文と添付ファイルが格納されます。メール本文中の画像も添付ファイルとして扱われます:bulb:

![image02.png](https://qiita-image-store.s3.amazonaws.com/0/12023/58edaeba-3ba1-c9dd-ca0b-7a0c409c90da.png)

PDF 化したメール本文は↓な感じです。添付ファイルはリスト形式でドライブ内の実ファイルにリンクが張られます。

![image03.png](https://qiita-image-store.s3.amazonaws.com/0/12023/9a4fc135-35af-c084-828f-c026e34dcc95.png)



# 3. スクリプトの解説
## 3.1. 新規スクリプト（GAS プロジェクト）を作る

まずは [script.google.com](https://script.google.com/) から新規の GAS プロジェクトを作ります。超久々に触るのですが GAS は開発環境とかの準備が不要だったりという点で手間がかからずサクッと開発できるので好きです:grinning:

![image00.png](https://qiita-image-store.s3.amazonaws.com/0/12023/44e27ce8-341e-922e-88cb-238c3aedd8a7.png)


## 3.2. マニフェストを書く

次に、これもいつの間にか GAS に追加された機能なのですが、マニフェストを書きます。スクリプトエディタのメニューから［表示］ー［マニフェスト ファイルを表示］で作成します。このマニフェストにアドオンがユーザーリソースの何にアクセスするかの OAuth スコープを記述します。

マニフェストの説明については、「[Google Apps Scriptに追加されたマニフェストについて](https://qiita.com/tanaike/items/b3538e7550555ebb51f5)」に詳しく解説されているので割愛することにします。

```javascript:appsscript.json
{
	"oauthScopes": [
		"https://www.googleapis.com/auth/gmail.addons.execute", 
		"https://www.googleapis.com/auth/gmail.readonly", 
		"https://www.googleapis.com/auth/gmail.modify", 
		"https://mail.google.com/", 
		"https://www.googleapis.com/auth/drive", 
		"https://www.googleapis.com/auth/script.external_request", 
		"https://www.googleapis.com/auth/userinfo.email"
	],
	"gmail": {
		"name": "Gmail Add-on Drive Export", 
		"logoUrl": "{アドオンのアイコンが配置された URL}", 
		"contextualTriggers": [
			{
				"unconditional": {}, 
				"onTriggerFunction": "buildAddOn"
			}
		], 
		"openLinkUrlPrefixes": [
			"https://mail.google.com/"
		],
		"primaryColor": "#4285F4", 
		"secondaryColor": "#4285F4", 
		"version": "TRUSTED_TESTER_V1"
	}
}
```

logoUrl で指定したアイコンは Gmail サイドバーの↓に表示されます。アイコンなどの静的ファイルは Google Cloud Storage(GCS) などに配置しておくとキャッシュも効いて良いですょ♪

![image.png](https://qiita-image-store.s3.amazonaws.com/0/12023/e20b34a1-9305-df26-29ed-2de340547fef.png)


## 3.3. メインスクリプトを書く

はじめにアドオンの UI を構築するコンテキストトリガー関数を実装します。この関数はマニフェストの `onTriggerFunction` フィールドで指定します。このケースでは `buildAddOn` 関数がそれになります。

Gmail アドオンはサイドバーペインとして Gmail UI に表示されます。モバイル（現在のところは Android 版 Gmail アプリのみ対応）ではメニューを介して別のアクティビティ ウィンドウとして表示されます。UI 部分はガイドラインが[公式ドキュメント](https://developers.google.com/gmail/add-ons/concepts/cards)にあるので目を通しておくと良いでしょう。

それでは具体的に中身のコードについて解説します。GAS においては Card サービスを使用して、アドオンの外観と動作を定義するコードを記述していきます。今回は以下のような［Drive Export］ボタンと、メールの送信者、添付ファイルの個数を表示するだけの単純な UI になります。

![sample.png](https://qiita-image-store.s3.amazonaws.com/0/12023/8943180d-9a2f-a9b2-9f59-8727fa44b685.png)

```javascript:main.gs
/**
 * マニフェスト 'onTriggerFunction' フィールドで指定されたアドオン起動トリガー
 * 
 * @param {Object} イベントオブジェクト
 * @return {Card} カードオブジェクト
 */
function buildAddOn(e){
	var accessToken = e.messageMetadata.accessToken;
	GmailApp.setCurrentMessageAccessToken(accessToken);

	var messageId = e.messageMetadata.messageId;
	var message = GmailApp.getMessageById(messageId);
	var subject = message.getSubject();					// 件名
	var from = message.getFrom();						// 送信元
	var attachments = message.getAttachments();			// 添付ファイル

	// UI を作る
	var buttonSet = CardService.newButtonSet();
	var exportButton = CardService.newTextButton()
		.setText('Drive Export')
		.setOnClickAction(CardService.newAction()
			.setFunctionName('driveExport')
			.setParameters({'messageId': messageId}));

	var card = CardService.newCardBuilder()
		.setHeader(CardService.newCardHeader()
			.setTitle(subject))
		.addSection(CardService.newCardSection()
			.addWidget(CardService.newKeyValue()
				.setTopLabel('送信元')
				.setContent(from))
			.addWidget(CardService.newKeyValue()
				.setTopLabel('添付ファイル')
				.setContent(attachments.length + ' 個'))
			.addWidget(exportButton))
		.build();

	return card;
}

/**
 * 指定されたメッセージ識別子に対応するメール本文と添付ファイルを Google ドライブに格納する
 * 
 * @param {Object} イベントオブジェクト
 */
function driveExport(e){
	var messageId = e.parameters['messageId'];
	var message = GmailApp.getMessageById(messageId);

	var driveFolder = 'Gmail Export';
	var folders = DriveApp.getFoldersByName(driveFolder);
	var folder = folders.hasNext() ? folders.next() : DriveApp.createFolder(driveFolder);

	var html = '';
	html += 'From: ' + message.getFrom() + '<br />';
	html += 'To: ' + message.getTo() + '<br />';
	html += 'Date: ' + message.getDate() + '<br />';
	html += 'Subject: ' + message.getSubject() + '<br />';
	html += '<hr />';
	html += message.getBody().replace(/<img[^>]*>/g, '');
	html += '<hr />';

	var attachments = [];
	var atts = message.getAttachments();
	for ( var i = 0; i < atts.length; i++ ) attachments.push(atts[i]);

	if ( attachments.length > 0 ) {
		var footer = '<strong>添付ファイル</strong><ul>';
		for ( var i = 0; i < attachments.length; i++ ) {
			var file = folder.createFile(attachments[i]);
			footer += '<li><a href="' + file.getUrl() + '">' + file.getName() + '</a></li>';
		}
		html += footer + '</ul>';
	}

	var tempFile = DriveApp.createFile('temp.html', html, 'text/html');
	folder.createFile(tempFile.getAs('application/pdf')).setName(message.getSubject() + '.pdf');
//	tempFile.setTrashed(true);
	deleteFile(tempFile.getId());
}

/**
 * 指定したファイルを Google ドライブから完全削除する
 * 
 * @param {String} ファイル ID
 */
function deleteFile(fileId){
	var token = ScriptApp.getOAuthToken();
	var response = UrlFetchApp.fetch(Utilities.formatString('https://www.googleapis.com/drive/v3/files/%s', fileId), {
		method: 'delete',
		headers: {
			'Authorization': 'Bearer ' + token
		},
		muteHttpExceptions: true
	});
}
```

次に［Drive Export］ボタン押下時の処理を書きます。この例では `driveExport` 関数になります。引数のイベントオブジェクトでメッセージ ID を受け取り GAS の Gmail サービスを使ってメールの中身にアクセスします。
メール本文を PDF 化したり、添付ファイルをドライブに保存する仕組みは、ちょうどイイ感じのスクリプトをネットで見つけることができたので、それを~~パクり~~参考にしました (・ω<)

* [Save Gmail Messages as PDF – Google Apps Script](https://ctrlq.org/code/19117-save-gmail-as-pdf)

このスクリプトでは、メール本文を PDF に変換するため、一時的に html ファイルを作って、最後にゴミ箱:wastebasket:に入れていますが、ゴミ箱が溢れちゃうとイヤなので、完全に削除させるため `deleteFile` 関数を加えて改良しました。GAS の組み込みにはファイルを完全削除する機能はないので Drive API を直接叩いて削除します。



# 4. アドオンをデプロイする

作成したアドオンをデプロイします。スクリプトエディタのメニューから［公開］ー［マニフェストから配置］を選択すると、以下のような画面が表示されます。
Latest Version （最新バージョン）の「Get ID」からデプロイメント ID を表示して控えておきます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/12023/3364f228-031a-5037-aade-16fd35f89969.png)



# 5. Gmail にアドオンをインストする

Gamil アドオンは Gamil もしくは G Suite Marketplace 経由でインストールすることができます。後者は以下のようなマーケットプレイスで、すべての Gmail ユーザーに公開することができますが Google に申請や審査が必要だったりします。グーグルへの申請フォームは[こちら](https://docs.google.com/forms/d/e/1FAIpQLSdQD9F5TXZvlpQcY1I6fvaZqLvULG1THhou3y5IpRRwjgwZYg/viewform)になりますが、まだまだ数は少ないですねー

余談ですが、マーケットプレイスへの公開は過去に Google Sheets 向けの電子印鑑アドオン（[DigitalStamp4Sheet](https://chrome.google.com/webstore/detail/digitalstamp4sheet/clbokgpmjffeicniaiebeabeejgdpdha?hl=ja)）を作ったことがありますが、審査を通すのに苦労した経験があります:disappointed_relieved:

![MarketPlace.png](https://qiita-image-store.s3.amazonaws.com/0/12023/12198d6b-621a-0083-5add-65bf7fac3d60.png)

今回は試作品で、一部の人のみなので、未公開アドオンとして Gmail にインストールします。Gmail の設定画面で［アドオン］タブから「アドオン」の **ご利用中のアカウントで、デベロッパー アドオンを有効にします** にチェックを入れます。

![image22.png](https://qiita-image-store.s3.amazonaws.com/0/12023/5b45c4ac-d054-e0ce-6237-cfd10e731aab.png)

次に「デベロッパー アドオン」で先ほど控えたデプロイメント ID を入力してインストールします。

![image12.png](https://qiita-image-store.s3.amazonaws.com/0/12023/84286827-4360-0a7f-6ee9-579952fdb570.png)

インストールが成功すると、「インストール済みのデベロッパー アドオン」にインストールした自分のアドオンが表示されるようになります。

未公開アドオンの注意点として G Suite 組織のユーザーは、同じ組織内のユーザーが作成した未公開のアドオンのみをインストールして実行できますが GAS プロジェクトそのものをドメイン全体で共有（閲覧権限でOK）しておかないと、次のように **無効なアドオン** と認識され、自分以外の他の人は利用することができません:confounded:

![image00.png](https://qiita-image-store.s3.amazonaws.com/0/12023/36674bac-66ba-021f-f9eb-21515749ce49.png)

ダッシュボードで次のように表示されていれば、ドメイン内で利用可能です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/12023/1a747c83-8619-6266-1d4d-0dc93984eb9d.png)



# 6. アドオンのアクセスを承認する（初回のみ）

インストール後の初回は、アドオンがユーザーの Gmail にアクセスしてもよいかを許可するための **アクセスを承認** が必要になります。

![image02.png](https://qiita-image-store.s3.amazonaws.com/0/12023/2a56e8b2-a8b1-8150-bf81-fa7aaef6105f.png)

承認が済めばアドオンはいつでも使えるようになります:thumbsup:



# 7. おわりに、、、

勉強会やイベントでセッションを聴くだけでは技術は身に付きません。自分でドキュメント読んで、コード書いて、わからないことはネットで調べてみたりして、手を動かして実体験することが大切です！

Gmail アドオンには[クイックスタート](https://developers.google.com/gmail/add-ons/guides/quickstart)というチュートリアルが用意されています。英語でハードルが高いと感じられる方は以下の記事を併せて読むと良いです:point_up:

* [Gmail Add-on 入門 (デベロッパーアドオンの作成とインストール)](https://mseeeen.msen.jp/quickstart-with-gmail-add-on/)

全国の GAS ユーザーのみなさん、Gmail だけでなく Sheets, Docs, Forms, Slides で日本発のアドオンを増やして G Suite をより便利にしていけたら良いですね！:stuck_out_tongue_closed_eyes: