突然の在宅勤務!始業・終業の通知メールをGmailで自動化してみた
コロナウィルス問題でいきなりテレワークになった方も大勢いらっしゃると思います。
勤怠管理のために始業・終業時にメールを送ってこいという古風な会社・組織もあると思います。
多分、急遽、在宅勤務を開始した公的機関にこの無駄なルールを作っているところが多いと思いますが。。。。
とはいってもルールに逆らっていいことはないので、作業を自動化してしまいましょう。
Gmailとそれを自動的に操作するGoogle Apps Script(GAS)というモノを使ってサックとプログラミングします。
プログラミングといってもコピペでいけちゃいますのでご安心ください!!
Google Apps Script(GAS)とは何か?簡単解説!
GmailやGoogleカレンダーなどはほとんどの人が使ってみたことがあると思います。
加えて、Googleスプレッドシート、Googleドキュメントなど、Googleは非常に便利なサービスをたくさん運営しています。
これらのGoogleのサービスをGoogle Apps Scriptを使えば定期実行したり自動化したり、複雑な作業をしたりすることができるようになります。
- 定期実行
- 自動化
- 複雑な作業
Google Apps Script(GAS)はどうやったら使える?
⇩Google Apps Scriptにアクセスします。
https://script.google.com/home
グーグルアカウントを持っていて、ログインしていれば、Google Apps ScriptというWebアプリケーションのページに入ることができます。
新しいプロジェクトをクリックします。
そうするとスクリプトを作成するページに飛びます。
プロジェクト名を「始業自動メール」とかに変更します。
コード.gsのところに関数を記述していくことになります。
このコードのところに後で紹介するスクリプトをコピペすることで、定期自動メール送信は行えるようになりますので、プログラミングしなきゃいけないの?という方は安心してください。
実際にGASを使って定期的にGメールを送信する!
プログラムは以下です。ほとんどコピペで自動メール送信を行えるようになります。
この例では、始業メールを'office-staff@konyagayamada.co.jp'に送ります。
送信先のメールアドレス (to)
メールのタイトル (title)
メールの本文 (body)
を用意してください。
function mail() {
var today = new Date();
var array = ['日','月','火','水','木', '金', '土']
var to = 'office-staff@konyagayamada.co.jp';
var title = (today.getMonth()+1) + "月" + today.getDate() + "日" + "("+ array[today.getDay()] + "曜日)" + " 8:30から始業します。";
var body = "本日はテレワークを行います。\n"+(today.getMonth()+1) + "月" + today.getDate() + "日" + "("+ array[today.getDay()] + "曜日)" + " 8:30から始業します。\nよろしくお願いします。\n\nアプリンゴ\n";
var aliases = GmailApp.getAliases();
GmailApp.sendEmail(to, title, body, {'from': aliases[0], 'name': 'Tadano Hito','cc': 'syomu@konyagayamada.co.jp','bcc': 'myself@gmail.com'});
}
function isBusinessDay(date){
if (date.getDay() == 0 || date.getDay() == 6) {
return false;
}
const calJa = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com');
if(calJa.getEventsForDay(date).length > 0){
return false;
}
return true;
}
function setTrigger(){
delTrigger();
var setTime = new Date();
setTime.setHours(8);
setTime.setMinutes(30);
if (isBusinessDay(setTime)){
ScriptApp.newTrigger('mail').timeBased().at(setTime).create();
}
}
function delTrigger() {
var triggers = ScriptApp.getProjectTriggers();
for(var i=0; i < triggers.length; i++) {
if (triggers[i].getHandlerFunction() == "mail") {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
コード.gsの部分に上記のスクリプトを貼り付けます。
スクリプトを丁寧に解説
このスクリプトにはfunction ~で始まる関数が4つあります。
mail()
,
isBusinessDay(date)
,
setTrigger()
,
delTrigger()
です。
mail()
実際に送信先にメールを送信する関数。
to は送信先のメールアドレス
title はメールのタイトル
body はメールの内容
文面に改行を入れたい場合は改行コード"\n"を文中に入れます。
実情に合わせて、書き換えてください。
'from'・・・ Gmailに別のメールアドレスを設定している場合(エイリアス)、そのエイリアスのアドレスを使用することができます。
'name'・・・送信元の名前を設定できます。
'cc'・・・ccで送りたいアドレスはここに設定します。
'bcc' ・・・bccもできます。
↓Apps Scriptのドキュメント
https://developers.google.com/apps-script/reference/gmail/gmail-app
isBusinessDay(date)
日付dateが営業日かどうか判定する関数。営業日であればtrueを返し、土日や祝日であればfalseを返す。
営業日・・・土日や祝日を除いた平日のこと
setTrigger()
Google Apps Scriptでメールを送信したい時刻にトリガーを設定する関数。
このスクリプトだと
setTime.setHours(8);
setTime.setMinutes(30);
のところで、8時30分にメールを送信するように設定されている。
delTrigger()
設定されたトリガーがそのまま残るので、setTriggerの最初で呼び出して、過去のトリガーを削除するための関数。setTrigger関数の最初の行で呼び出されている。
トリガーの仕組みとその設定方法
定期実行を行うためにはトリガーの仕組みと設定方法を理解する必要があります。
トリガーの設定のために上記のアイコンをクリックします。
トリガー設定画面に移りますので、トリガーを追加をクリックします。
設定ができたら保存します。
このようにしてトリガーを設定すると以下のようなことが行われるようになります。
午前4時〜5時の間にsetTrigger()を実行されます。そうすると、自動的にsetTriggerは8時30分にmail()を実行するようにトリガーを設定します(setTrigger関数内のnewTrigger('mail')が新しいトリガーを作ります)。そして8時30分になるとmailが実行されます。
毎日この流れが繰り返され、定時にメールを送信できるようになりました。
もう一つ「終業自動メール」プロジェクトを同じように作成すれば終業時にもメールを送れるようになります。
Google Apps Scriptの簡単な使い方
上で紹介しきれなかった補足情報をここで紹介します。
関数を選んで実行
関数を選んで実行できます。
簡単に関数をテストできますね。
例えばmail関数を選んで実行を押すと、メールが送信されます(テストする際は送信先を自分のメールアドレスなどにしておきましょう。さもないと、不審なメールを相手に送ってしまいます)。
ログを出力したい!
Logger.log()でログを出力することができます。
例えば変数setTimeの中身を確認したい場合、
Logger.log(setTime);
で中身を確認できます。
関数を実行した後で、Macの場合だと、
command + enter
を押すと、ログ出力画面に切り替わります。
Gmailのエイリアスについて
Gmailでのエイリアス(別名)メールアドレスの設定方法を解説します。
これを設定することで、例えば、Gmailから送っているのにあたかも職場のメールアドレスから送信しているように見せかけることができます。
Gmailの設定をクリックします。
アカウントとインポートから他のメールアドレスを追加できます。
'from'にこのアドレスを指定すると、そのメールアドレスから送信されたようにすることができます。
Googleカレンダーの設定について
古くからGoogleカレンダーを利用しているユーザーのかたの場合、日本の祝日を参照するIDである
'ja.japanese#holiday@group.v.calendar.google.com'
が異なることがある様です。
その確認方法について簡単に説明しておきます。
Googleカレンダーには
- 他の人のカレンダー
- 祝日(日本の祝日やキリスト教の祝日など)
- スポーツの日程(日本の野球チームの日程などもある、例えば巨人とか)
- 月の位相
などを自分のカレンダーに追加できます。
上で紹介した自動始業・終業メールでは"日本の祝日"からデータを利用しています。
自分のGoogleカレンダーを開いて、下の方にあるその他のカレンダーから”日本の祝日”にカーソルを載せます。
設定をクリックします。
カレンダーIDが紹介したIDと同じか確認してみてください。
コメント
こちらの記事にあるコードをコピペしてGASで自動送信の設定をしてみたのですが、うまくいきません
何が原因でしょうか?
エラーメッセージは
2020/08/20 4:54:52 エラー TypeError: Cannot read property ‘getEventsForDay’ of null
at isBusinessDay(メール自動送信:16:12)
at setTrigger(メール自動送信:27:7)
実際に書いたコードは以下のようになります。
function mail() {
var today = new Date();
var array = [‘日’,’月’,’火’,’水’,’木’, ‘金’, ‘土’]
var to = ‘送り先’;
var title = “件名”;
var body = “本文”;
var aliases = GmailApp.getAliases();
GmailApp.sendEmail(to, title, body, {‘from’: aliases[0], ‘name’: ‘名前’});
}
function isBusinessDay(date){
if (date.getDay() == 0 || date.getDay() == 6) {
return false;
}
const calJa = CalendarApp.getCalendarById(‘ja.japanese#holiday@group.v.calendar.google.com’);
if(calJa.getEventsForDay(date).length > 0){
return false;
}
return true;
}
function setTrigger(){
delTrigger();
var setTime = new Date();
setTime.setHours(7);
setTime.setMinutes(30);
if (isBusinessDay(setTime)){
ScriptApp.newTrigger(‘mail’).timeBased().at(setTime).create();
}
}
function delTrigger() {
var triggers = ScriptApp.getProjectTriggers();
for(var i=0; i < triggers.length; i++) {
if (triggers[i].getHandlerFunction() == "mail") {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
ご検討宜しくお願い致します。
コメントありがとうございます。
Googleカレンダー側の設定の問題だと思います。
http://syasuda.com/d/blog/2016/11/googleid.html
の人と同じような状態かと思います。
かなり古くからgoogleカレンダーを利用している人が参照している日本の祝日カレンダーは
‘ja.japanese#holiday@group.v.calendar.google.com’
と違うようです。
googleカレンダーの他のカレンダーの設定から
カレンダーIDを見てみてください。
また、一度日本の祝日カレンダーの登録を解除して、再登録すると
カレンダーIDを
‘ja.japanese#holiday@group.v.calendar.google.com’
に変更することができると思います。
記事の最後尾に説明を図いりで追記したので参考にしてみてください。
こんにちは。大変参考になる記事、ありがとうございます。
記事のスクリプトをコピペして、[isBusinessDay]のでバック実行をしたところ
下記のエラーがでてきました。
17:54:18 エラー
TypeError: Cannot read property ‘getDay’ of undefined
isBusinessDay @ コード.gs:12
ちなみに、Googleカレンダーの「日本の祝日」の設定を確認したところ、記事に記載の
‘ja.japanese#holiday@group.v.calendar.google.com’
で登録されています。
原因について考えられる事がありましたら、ご教示いただけましたら幸いです。
実際に作成したスクリプトは以下のとおりです。
function mail() {
var today = new Date();
var array = [‘日’,’月’,’火’,’水’,’木’, ‘金’, ‘土’]
var to = ‘メールアドレス’;
var title =”件名”;
var body = “〇〇〇〇 \n”+(today.getMonth()+1) + “月” + today.getDate() + “日” + “(“+ array[today.getDay()] + “曜日)” + “通常通り出勤しました。”;
var aliases = GmailApp.getAliases();
GmailApp.sendEmail(to, title, body, {‘from’: aliases[0], ‘name’: ‘氏名’,’bcc’: ‘メールアドレス’});
}
function isBusinessDay(date){
if (date.getDay() == 0 || date.getDay() == 6) {
return false;
}
const calJa = CalendarApp.getCalendarById(‘ja.japanese#holiday@group.v.calendar.google.com’);
if(calJa.getEventsForDay(date).length > 0){
return false;
}
return true;
}
function setTrigger(){
delTrigger();
var setTime = new Date();
setTime.setHours(16);
setTime.setMinutes(30);
if (isBusinessDay(setTime)){
ScriptApp.newTrigger(‘mail’).timeBased().at(setTime).create();
}
}
function delTrigger() {
var triggers = ScriptApp.getProjectTriggers();
for(var i=0; i < triggers.length; i++) {
if (triggers[i].getHandlerFunction() == "mail") {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
お忙しいところ恐縮ですが、何卒よろしくお願いいたします。
isBusinessDay関数を単体でデバック実行したということでしょうか?
そうだとするとdateには何も入っていないので、
TypeError: Cannot read property ‘getDay’ of undefined
のようなエラーが出そうな気もします。
var date = new Date();
date.setHours(16);
date.setMinutes(30);
のようにdateを設定して
isBusinessDay関数に渡す必要があるかもしれません。
詳細がわからないので、トンチンカンな答えかもしれません。
参考になる記事をありがとうございます。
以下のコードを設定し、トリガーでsetTriggerを午前1-2時に実行されるように設定したのですが、mail()が意図とは異なる時間に送られてしまいました。
以下の例だと、午前9時に送ってほしいところ、実際には午前2時前にメールが送付されました。
function mail() {
var today = new Date();
var to = ‘address@domain.com’;
var title = “始業報告”;
var body = “始業”;
GmailApp.sendEmail(to, title, body, {‘cc’: ‘address@domain.com’});
}
function isBusinessDay(date){
if (date.getDay() == 0 || date.getDay() == 6) {
return false;
}
const calJa = CalendarApp.getCalendarById(‘ja.japanese#holiday@group.v.calendar.google.com’);
if(calJa.getEventsForDay(date).length > 0){
return false;
}
return true;
}
function setTrigger(){
delTrigger();
var setTime = new Date();
setTime.setHours(9);
setTime.setMinutes(00);
if (isBusinessDay(setTime)){
ScriptApp.newTrigger(‘mail’).timeBased().at(setTime).create();
}
}
function delTrigger() {
var triggers = ScriptApp.getProjectTriggers();
for(var i=0; i < triggers.length; i++) {
if (triggers[i].getHandlerFunction() == "mail") {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
別の例で、setTrigger内で以下の設定をし、トリガーは同じく午前1-2時に実行と設定していたところ、午前8時にメールが送付されてしまいました。
setTime.setHours(18);
setTime.setMinutes(00);
何か間違えておりますでしょうか?
お忙しい中申し訳ありませんが、ご教示いただければ幸いです。
トリガーの設定はできていますでしょうか?
トリガー設定画面で実行する関数はsetTriggerです。mailではありません。
本文にも書いてますが、
トリガー
⇩
setTrigger 関数 実行
⇩
mail関数 実行 ⇨ メール送信
です。
また、変なトリガーが設定されていないか確認してみてください。
https://script.google.com/home/triggers
変なトリガーが保持されているようであれば削除してみてください。
うまく行くまでは自分のサブメールアカウントでテストをするのをオススメします。
早速返信くださりありがとうございます。
はい、トリガー設定画面ではsetTriggerを時間主導型・日付ベース・午前1-2時に実行、としております (18時にメールを送ろうとした分も同一です)。
本日早朝にこれが1回実行され、その結果生成された、mailを実行する時間主導型・特定の日時に実行 (ただし、この日時が何故か昨日の11:50と全く関係ない時間)、のトリガーも残っております (「前回の実行」は「無効」になっております)。
どこまで関係あるかは不明ですが、実行時間帯を午前4-5時に修正してみました。
また、数日様子を見ることとします。
参考になる記事をありがとうございます。
土日祝日関係なく毎日送る設定にしたいのですがどのようにすればいいのでしょうか?
if (isBusinessDay(setTime)){ ScriptApp.newTrigger(‘mail’).timeBased().at(setTime).create(); }
を
ScriptApp.newTrigger(‘mail’).timeBased().at(setTime).create();
にすればOKです。
ビジネスデイの判定をしないで、実行をすることになります。
早速教えていただきありがとうございます。
素晴らしい記事ありがとうございます。
すいません、1点教えて下さい。
有給休暇や、会社の創立記念休日などカレンダーにて意図せぬ休暇休日となった場合のメール送信を止めたい場合、もしくはあらかじめこの日は送信したくないなどと決めたい場合はどうしたら良いのでしょうか?
お教えいただければ幸いです。何卒宜しくお願いします。
if (date.getDay() == 0 || date.getDay() == 6) { return false; }
の下に以下を付け加えると良いでしょう。
Anniversary_date = new Date(‘2022-10-25’)
if (date.getDay() == Anniversary_date) { return false; }
みたいにすると創立記念日の2022-10-25は営業日でないと判定されてメールは送られません。
急遽休む場合などはトリガーを削除するのが確実で安全だと思います。
ご教授いただきありがとうございます!
チャレンジしてみます!
とても詳しい記事の提供ありがとうございます!
タイトル「〇月出勤連絡」の同一スレッド内に送信したいのですが、これは難しいでしょうか?
調べても分からなかったので、ご教示頂ければと思います
とても参考になります!
一つ質問なんですが、題名「〇月出勤連絡」というタイトルの同一スレッド内に返信は可能でしょうか?
常に自分しか書きこまないので、自身に対しての返信になります
可能であればご教示頂ければと思います
宜しくお願いします
// Log the subject lines of up to the first 50 emails in your Inbox
var threads = GmailApp.getInboxThreads(0, 50);
for (var i = 0; i < threads.length; i++) { if(threads[i].getFirstMessageSubject() ==="〇月出勤連絡"){ threads[i].replay("メッセージ") }; } かな??? 試していないので自己責任で試してみてください。 referenceに色々載ってますよ。 https://developers.google.com/apps-script/reference/gmail/gmail-app#getInboxThreads(Integer,Integer)
https://developers.google.com/apps-script/reference/gmail/gmail-thread#reply(String)
返信ありがとうございます!
こちらで試してみたところ、以下のコードで返信するまでは出来ました
しかし、タイトルに一致するメール全てに返信してしまうため、日に日に送信数が倍々で増えてしまいます
最新のメールにのみ返信は可能でしょうか?
自分なりに考えたり調べてみましたが解決できませんでした
var threads = GmailApp.getInboxThreads(0, 10);
for (var i = 0; i < threads.length; i++)
{
var thread=threads[i];
if(thread.getFirstMessageSubject().includes ("テレワーク出勤") )
{
var message = thread.getMessages()[0]; // Get first message
Logger.log(message.getDate()); // Log date and time of the message
thread.reply("abcd");
}
(先日のコメントが入っていないと思い、二つ書きこんでしまっていましたごめんなさい)
状況が完全につかめていないですが、
条件に合うthreadをひとつ見つけたらループから出るようにすれば良いかと思います。
if(thread.getFirstMessageSubject().includes (“テレワーク出勤”) )
{
var message = thread.getMessages()[0]; // Get first message
Logger.log(message.getDate()); // Log date and time of the message
thread.reply(“abcd”);
break;
}
と最後にbreakを入れると、条件に合うthreadひとつにreplyして終わりになります。
試していないので、試してみてください。
説明不足で申し訳ありません
現在は手動で「3月出勤連絡」というタイトルのメールに対して、土日祝日以外の平日は毎日セルフリプライでメールをしています
何かあった時や、後で見返す場合にスレッド化されていた方が便利なためです
このスレッドには他の誰かや上司が返信する事は無く、自分しかメールをしません
ありがとうございます!
上記breakを付けた所、返信は一回で終わりになりました!
しかし、常に以下二つの条件が発生しているようです
(1)新規スレッドでタイトル「〇月出勤連絡」本文「本日はテレワークを行います。」のメール
(2)検索条件で上記タイトルに一致するので本文「abcd」というメールを(1)に対して返信
これにより、常に(1)とそれにリプを付ける(2)の2つのみのメールになります
恐らくこれが最後の質問になりますのでどうかご教示頂ければと思います
(2)の条件が発動した場合(1)の処理は行わないようにしたいです
コード全体は以下のようになっています(アドレスなど一部改変しています)
function mail() {
var today = new Date();
var array = [‘日’,’月’,’火’,’水’,’木’, ‘金’, ‘土’]
var to = ‘***@gmail.com’;
var title = ” 〇月テレワーク出勤” ;
var body = “本日はテレワークを行います。”;
var aliases = GmailApp.getAliases();
GmailApp.sendEmail(to, title, body, {‘from’: aliases[0], ‘name’: ‘Tadano Hito’,’cc’: ‘***@gmail.com’,’bcc’: ‘***@gmail.com’});
}
function isBusinessDay(date){
if (date.getDay() == 0 || date.getDay() == 6) {
return false;
}
const calJa = CalendarApp.getCalendarById(‘ja.japanese#holiday@group.v.calendar.google.com’);
if(calJa.getEventsForDay(date).length > 0){
return false;
}
return true;
}
// Log the subject lines of up to the first 50 emails in your Inbox
var threads = GmailApp.getInboxThreads(0, 10);
for (var i = 0; i < threads.length; i++)
{
var thread=threads[i];
if(thread.getFirstMessageSubject().includes ("テレワーク出勤") )
{
var message = thread.getMessages()[0]; // Get first message
Logger.log(message.getDate()); // Log date and time of the message
thread.reply("abcd");
break;
}
}
function setTrigger(){
delTrigger();
var setTime = new Date();
setTime.setHours(8);
setTime.setMinutes(55);
if (isBusinessDay(setTime)){
ScriptApp.newTrigger('mail').timeBased().at(setTime).create();
}
}
function delTrigger() {
var triggers = ScriptApp.getProjectTriggers();
for(var i=0; i < triggers.length; i++) {
if (triggers[i].getHandlerFunction() == "mail") {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
宜しくお願い致します
全く状況が掴めませんが、mail関数内でflagを立てて、こうするのかな?
threadにreplyしたときにはmailを送らない。
function mail() {
var today = new Date();
var array = [‘日’,’月’,’火’,’水’,’木’, ‘金’, ‘土’]
var to = ‘***@gmail.com’;
var title = ” 〇月テレワーク出勤” ;
var body = “本日はテレワークを行います。”;
var aliases = GmailApp.getAliases();
var flag = 0
var threads = GmailApp.getInboxThreads(0, 10);
for (var i = 0; i < threads.length; i++) { var thread=threads[i]; if(thread.getFirstMessageSubject().includes ("テレワーク出勤") ) { var message = thread.getMessages()[0]; // Get first message Logger.log(message.getDate()); // Log date and time of the message thread.reply("abcd"); flag = 1 break; } } if ( flag === 0 ){ GmailApp.sendEmail(to, title, body, {‘from’: aliases[0], ‘name’: ‘Tadano Hito’,’cc’: ‘***@gmail.com’,’bcc’: ‘***@gmail.com’}); } }
ありがとうございます!
あれから色々試してみたんですが、このコードを追加するとエラーで保存が出来なくなります・・・
「mail を実行しようとしましたが、プロジェクトを保存できませんでした。」
https://script.google.com/home/projects/1s4PB714xofObAeNn4PZaomn-_QhkmLmfWM9Uggf2_TfwoYdxRZkOeMHr/edit
こんにちは。大変参考になる記事、ありがとうございます!
おかげで自動メールを飛ばせるようになりました。
初歩的な質問で大変恐縮なのですが、1日に内容の違うメールを2通3通と飛ばすためには、どうしたらよろしいでしょうか?
新規スクリプトを作成したところ、後者のスクリプトのみ実行されてしまいました。
不勉強で申し訳ないのですが、もし分かりましたらご教授いただけますと幸いです。
よろしくお願いいたします。
プロジェクト単位なので、新しいプロジェクトを作ってください。
お返事ありがとうございます!
解決しました!大変助かります。
大変ありがとうございます。つかわせていただいています。以前日曜配信についても教えていただきました。
今回、行間を開ける場合どのようにすればいいのでしょうか?という質問です。
\n②大どんでん返し見せてやれ想像を超えた奇跡が俺に起こる。そして物語は既に始まっている\n”
+(today.getMonth()+1) + “月” + today.getDate() + “日” + “(“+ array[today.getDay()] + “曜日)” + ” ここから!”;
これでおこなうと、②大どんでん返し見せてやれ想像を超えた奇跡が俺に起こる。そして物語は既に始まっている
9月24日(日曜日) ここから!となります。
日付の上を1行空欄開けたいのです。忙しいと思いますが、教えてください。
よろしくお願いいたします。
“\n②大どんでん返し見せてやれ想像を超えた奇跡が俺に起こる。そして物語は既に始まっている\n\n”
+ (today.getMonth()+1) + “月” + today.getDate() + “日” + “(“+ array[today.getDay()] + “曜日)” + ” ここから!”;
日付の上に1行空けたい場合、\n を2つ続けて使用することで、空行が作成されます。
大変早く教えて頂きありがとうございます。
さっそくやってみます。
簡単にできました。
ありがとうございます。