Gmailに届いた添付ファイルをGASでSlackに流す
これは株式会社SUPER STUDIO Advent Calendar 2022の3日目の記事です。
ごきげんよう🌸
株式会社SUPER STUDIOでコーポレートエンジニアを担当している@bboobbaaです!
晩夏のとある日、QuickSightのユーザー作成・権限・課金に悩まされておりました。
そのときに、Amazon QuickSightのビジュアルを画像に変換するの記事を読んで、「Adminユーザーにメール送ってGASでSlackにファイル流せばええやん」と気付き実装しました。
GASとSlack APIを利用する初心者向けの記事となります。
まずはSlackのトークンの入手
Appを作成してTokenを入手します。
chat:write
とfile:write
の権限があればSlackの書き込みとアップロードができます。
const SLACK_TOKEN = 'xoxb-000000000000-000000000000-xxxxxxxxxxxx';
Gmailからレポートのスレッドの抽出
Class GmailAppより、スレッドの検索をします。
今回はQuickSightのレポートなので、Quicksight Report
で検索するとスレッドが抽出できます。
const threads = GmailApp.search('Quicksight Report', 0, 2);
第二引数の0
は開始スレッドのインデックス、第三引数の2
は返されるスレッドの最大数を設定しています。
スレッドの仕様についてはメールをスレッドごとにグループ化するマニュアルを参照ください。
Gmailのスレッドからメッセージの抽出
Class GmailAppより、スレッドからメッセージを取得します。
const messages = GmailApp.getMessagesForThreads(threads);
これでメッセージの連想配列となりました。
スレッドをループしてメッセージを取得
スレッドの中に複数メッセージが入っているので、ループを2回まわします。
// スレッドのループ
for (const i in messages) {
// スレッド内のメッセージのループ
for (const j in messages[i]) {
console.log(messages[i][j]);
}
}
メッセージの詳細は各関数を使って取得することになります。
例えばタイトルを取得したい場合は、messages[i][j].getSubject()
と書きます。
{ toString: [Function],
getDate: [Function],
getFrom: [Function],
refresh: [Function],
getHeader: [Function],
getId: [Function],
reply: [Function],
isStarred: [Function],
getSubject: [Function],
getReplyTo: [Function],
markRead: [Function],
getAttachments: [Function],
createDraftReply: [Function],
getBody: [Function],
getThread: [Function],
getTo: [Function],
forward: [Function],
star: [Function],
isDraft: [Function],
moveToTrash: [Function],
isInPriorityInbox: [Function],
createDraftReplyAll: [Function],
getPlainBody: [Function],
getRawContent: [Function],
isInTrash: [Function],
unstar: [Function],
markUnread: [Function],
isUnread: [Function],
isInInbox: [Function],
isInChats: [Function],
replyAll: [Function],
getCc: [Function],
getBcc: [Function] }
添付ファイルの取得
添付ファイル情報は、messages[i][j].getAttachments()
から取得できますが、配列でデータを持っているためもう一度ループを回します。
QuickSightの場合メールの添付にロゴもくっついてくるので、ロゴの場合は処理をスキップします。
for (const i in messages) {
for (const j in messages[i]) {
for (const attachment of messages[i][j].getAttachments()) {
if (attachment.getName() != 'logo.png') {
console.log(attachment);
}
}
}
}
Slackにファイルをアップロードする
files.upload
をつかってファイルをアップロードします。SlackはBearer認証です。
const payload = {
file: attachment,
channels: 'XXXXXXXXXXX',
}
const option = {
method: 'post',
payload: payload,
headers: {'Authorization': `Bearer ${SLACK_TOKEN}`},
}
JSON.parse(UrlFetchApp.fetch('https://slack.com/api/files.upload', option).getContentText());
このままだと、ファイルごとにSlackに投稿されてしまう
見た目的によろしくないですね。というわけでチャンネル情報を渡しません。
アップロードだけして、ファイルのURLだけいただきましょう。
let text = '';
~~
const payload = {
file: attachment,
}
~~
const upload = JSON.parse(UrlFetchApp.fetch('https://slack.com/api/files.upload', option).getContentText());
text += `${upload.file.permalink}\n`;
Slackに情報を投稿する
先程のURLをまとめたテキスト情報をchat.postMessage
を使ってSlackに投稿します。
let text = '';
~~
const payload = {
channel: 'XXXXXXXXXXX',
text: text,
}
const option = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(payload),
headers: {'Authorization': `Bearer ${SLACK_TOKEN}`},
}
UrlFetchApp.fetch('https://slack.com/api/chat.postMessage', option).getContentText();
あれれ〜おかしいぞ〜?実行するたびに毎回投稿される
投稿したら完了フラグ立てないと、実行したら毎回投稿してしまいます。
投稿したらstar()
でメッセージにスターを付けて、以降はisStarred()
でtrue
が返ってくるものは処理しないように判定しましょう。
最終的なコードはこんなかんじ
const SLACK_TOKEN = 'xoxb-000000000000-000000000000-xxxxxxxxxxxx';
const threads = GmailApp.search('Quicksight Report', 0, 2);
const messages = GmailApp.getMessagesForThreads(threads);
let text = '';
for (const i in messages) {
for (const j in messages[i]) {
if (!messages[i][j].isStarred()) {
for (const attachment of messages[i][j].getAttachments()) {
if (attachment.getName() != 'logo.png') {
const payload = {
file: attachment,
}
const option = {
method: 'post',
payload: payload,
headers: {'Authorization': `Bearer ${SLACK_TOKEN}`},
}
const upload = JSON.parse(UrlFetchApp.fetch('https://slack.com/api/files.upload', option).getContentText());
text += `${upload.file.permalink}\n`;
}
}
messages[i][j].star();
}
}
}
const payload = {
channel: 'XXXXXXXXXXX',
text: text,
}
const option = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(payload),
headers: {'Authorization': `Bearer ${SLACK_TOKEN}`},
}
if (text) UrlFetchApp.fetch('https://slack.com/api/chat.postMessage', option).getContentText();
GASのトリガーを設定して完了
よしなに関数組み立ててください!
Discussion