GASでGmailをSlackに通知する
動機
defaultの機能だとSlackbotにしか転送できない
channelごとの振り分けは有料のSlack appを導入しないといけない
目的
これやりたい
使い方
1分ごとでもいい気がするtakker.icon
そんなに周期速くて制限に引っかかったりしないのですか?
GASって制限ありましたっけ?
Google APIにAPI rateがあるんだから、GASにもあるか
まあ10分くらいでも十分そう
2021-03-27 05:19:48 できた
実装
もっともこの状態では使えないと思う
Slack APIが古い
詳しく調べないとtakker.icon
作り変えた
3つの段階に分ける
検索queryを元にメールを検索する
Slackの各channelに投稿する
投稿したメールを全て既読にする
設定変数を用意する
以下をセットにした配列
検索query生成関数
実行時の日付を受け取れるようにする
投稿先channelのweb hook
実装すること
Threadのうち、最新のメールだけを取り出す
これすこし実装が難しいかも
検索クエリで、threadから特定日時のものだけを取り出せるような仕組みがあればいいんだけど
Threadから最新のメールだけ返せばいいか
大抵は、一連メールが自動で最新のメールの本文末尾に書き込まれている
それ見りゃだいたいわかるだろtakker.icon
リッチにする
以下を先頭にcontextで入れる
送信日時
形式:Date: {date}
送信元:From: {address}
宛先:To: {address}
件名
メッセージのタイトルに入れるからいらない
code:main.gs(js)
function main() {
const now = (new Date()).getTime();
const threads = settings.flatMap(({ channel, query }) => {
const threads = GmailApp
.search(query(now));
console.log(send ${threads.length} messages);
console.log(query: ${query(now)});
return threads.map(thread => {
// 最新のメールだけ取得する
const message = thread.getMessages().pop();
send(message, channel);
return thread;
});
});
// 最後にまとめて既読にする
threads.forEach(thread => thread.markRead());
}
function send(message, channel) {
const body = {
text: message.getSubject(),
...blocks(
header(message.getSubject()),
context(mrkdwn([
Date: ${message.getDate()},
From: ${message.getFrom()},
To: ${message.getTo()},
].join('\n'))),
section(message.getPlainBody()),
),
};
try {
UrlFetchApp.fetch(channel, {
method: 'POST',
contentType: 'application/json',
payload: JSON.stringify(body),
muteHttpExceptions: true,
});
} catch (e) {
console.error(e);
}
}
code:settings.gs(js)
const settings = [
{
query: (today) => in:inbox is:unread after:${parseInt((today - 10 * 60 * 1000) / 1000)},
channel:'webhook URL',
},
];
code:blockKit.gs(js)
const em = text => _${text}_;
const strong = text => *${text}*;
const strike = text => ~${text}~;
const br = '\n';
const p = text => \n${text}\n;
const blockquote = text => > ${text};
const code = text => \`${text}\`;
const pre = text => \`\`\`${text}\`\`\`;
const a = ({href}, text) => <${href}${text ? |${text} : ''}>;
const hr = {type: 'divider'};
const blocks = (...items) => {return {blocks: items};};
const header = (text) => {
return {
type: 'header',
text: plain_text(text),
};
}
const section = (text, {plain = false} = {}) => {
return {
type: 'section',
text: plain ? plain_text(text) : mrkdwn(text),
};
};
const context = (...elements) => {
return {
type: 'context',
elements,
};
};
const mrkdwn = (text, {verbatim = true} = {}) => {
return {
type: 'mrkdwn',
text,
verbatim,
};
};
const plain_text = (text, {emoji = true} = {}) => {
return {
type: 'plain_text',
text,
emoji,
};
};
const image = (url, alt_text) => {
return {
type: 'image',
url,
...(alt_text ? {alt_text} : {}),
};
};