質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

ChatWork

業務の効率化を目的としたコミュニケーションツール。 グループチャット、ビデオ・音声通話、ファイル共有、タスク管理などの機能を備えています。マルチデバイス対応で、ブラウザだけでなくタブレットやスマートフォンでも利用可能です。

Q&A

解決済

1回答

344閲覧

GASを使ったスプレッドシートからChatworkに自動通知ツール

Rin22209853

総合スコア5

Google Apps Script

Google Apps ScriptはGoogleの製品と第三者のサービスでタスクを自動化するためのJavaScriptのクラウドのスクリプト言語です。

ChatWork

業務の効率化を目的としたコミュニケーションツール。 グループチャット、ビデオ・音声通話、ファイル共有、タスク管理などの機能を備えています。マルチデバイス対応で、ブラウザだけでなくタブレットやスマートフォンでも利用可能です。

0グッド

0クリップ

投稿2024/04/26 06:40

編集2024/04/26 06:46

実現したいこと

実現したいこと
スプレッドシートからChatworkに自動通知させたいです。
毎日9~10時をトリガー自動で実行する仕様にしています。
スプレッドシートE列に入れた実行予定日に基づいて通知を行います。

送りたい日と通知は下記の通りです
① E列に入れた実行予定日と同じ実行日のとき「当日通知」の文章を送る
② E列に入れた実行予定日の前日のとき「前日通知」の文章を送る
※ ただし土日祝日が実行予定日の前日の場合は「前営業日」の文章に変更して送付するイメージです。

また実行後に 実行日時(前日)|| 実行日時(当日)に実行日付を書き込みますが、
書き込まれている部分は再実行をしないようにしたいです。

発生している問題・分からないこと

GASでスプレッドシートからChatworkに自動通知するプログラムを組んでいます。
以下の条件分岐をさせて通知を行いたいのですが、当日通知が送られません。

送りたい日と通知は下記の通りです
① E列に入れた実行予定日と同じ実行日のとき「当日通知」の文章を送る
② E列に入れた実行予定日の前日のとき「前日通知」の文章を送る
※ ただし土日祝日が実行予定日の前日の場合は「前営業日」の文章に変更して送付するイメージです。

スプレッドシートは以下の構成です
実行している日は20230426です

N0. ||ルームID ||通知タイトル ||投稿内容 ||通知トリガー ||実行日時(前日)|| 実行日時(当日)
N0. ||ルームID ||通知タイトル ||投稿内容 ||2024/04/26||         ||
N0. ||ルームID ||通知タイトル ||投稿内容 ||2024/04/30||         ||
N0. ||ルームID ||通知タイトル ||投稿内容 ||2024/05/14||         ||      

該当のソースコード

GAS

1function sendExpenseNotification() { 2 // 現在の日付と時刻を取得 3 const today = new Date(); 4 Logger.log("今日: " + today.toLocaleString("ja-JP")); 5 const holidays = getHolidays(); 6 Logger.log("祝日: " + holidays.join(", ")); 7 8 // スプレッドシート読み込み 9 const ss = SpreadsheetApp.getActiveSpreadsheet(); 10 const sheet = ss.getSheetByName('自動通知'); 11 const lastRow = sheet.getLastRow(); 12 13 // ChatWorkAPIクライアント作成 14 const client = ChatWorkClient.factory({ token: token }); 15 16 // スプレッドシートに記載がある分だけ繰り返す 17 for (var i = 2; i <= lastRow; i++) { 18 const [roomId, messageItem, message, triggerDate, prevDayExecuted, todayExecuted, prevDayExecutedDate, todayExecutedDate] = sheet.getRange(i, 2, 1, 9).getValues()[0]; 19 if (!triggerDate) continue; 20 21 // F列に日付が入っている場合は前日実行済みなのでスキップ 22 if (prevDayExecutedDate) continue; 23 24 // G列に日付が入っている場合は当日実行済みなのでスキップ 25 if (todayExecutedDate) continue; 26 27 const reservationDate = new Date(triggerDate); 28 Logger.log("トリガー日: " + reservationDate.toLocaleString("ja-JP")); 29 30 // 実行日がトリガー日の前営業日より前の日付やトリガー日より後の日付には通知しない 31 const prevWeekday = getPreviousWeekday(today); 32 Logger.log("前営業日: " + prevWeekday.toLocaleString("ja-JP")); 33 if (today < prevWeekday || today >= reservationDate) { 34 continue; 35 } 36 37 // 通知トリガーが実行日の4日前より未来の場合は通知しない 38 const fourDaysBeforeTrigger = new Date(reservationDate); 39 fourDaysBeforeTrigger.setDate(reservationDate.getDate() - 4); 40 Logger.log("通知トリガーの4日前: " + fourDaysBeforeTrigger.toLocaleString("ja-JP")); 41 if (today < fourDaysBeforeTrigger) { 42 continue; 43 } 44 45 // 当日通知実行 46 if ( 47 isSameDate(today, reservationDate) && 48 !todayExecuted && 49 !prevDayExecutedDate && 50 !todayExecutedDate 51 ) { 52 Logger.log("■ 今日は実行日です。"); 53 // 当日通知を送信 54 const todayMsg = `※連絡※【本日は${messageItem}です】\n${message}`; 55 client.sendMessage({ 56 room_id: roomId, 57 body: todayMsg 58 }); 59 // 当日通知実行日時の記録 60 sheet.getRange(i, 7).setValue(today); 61 } 62 63 // 前日通知実行 64 const prevDay = new Date(reservationDate); 65 prevDay.setDate(reservationDate.getDate() - 1); 66 Logger.log("前日: " + prevDay.toLocaleString("ja-JP")); 67 68 // 前日が平日かどうかをチェック 69 const isPrevDayWeekday = isWeekday(prevDay); 70 // 前日が祝日かどうかをチェック 71 const isPrevDayHoliday = isHoliday(prevDay); 72 73 if (isPrevDayWeekday && !isPrevDayHoliday && !prevDayExecuted) { 74 Logger.log("■ 前日は平日のため前日通知日です"); 75 // 平日の場合、前日通知を送信 76 const prevDateMsg = `※連絡※【明日は${messageItem}です】\n${message}`; 77 client.sendMessage({ 78 room_id: roomId, 79 body: prevDateMsg 80 }); 81 // 前日通知実行日時の記録 82 sheet.getRange(i, 6).setValue(today); 83 } else if (!isPrevDayWeekday || isPrevDayHoliday) { 84 Logger.log("■ 前日は土日祝日のため前営業日通知日です"); 85 // 平日でない場合または祝日の場合、前営業日通知を送信 86 const prevWeekdayMsg = `※連絡※【${messageItem}につきまして】当日は休日のため、前営業日(${today.getMonth() + 1}月${today.getDate()}日)に投稿いたします。\n${message}`; 87 client.sendMessage({ 88 room_id: roomId, 89 body: prevWeekdayMsg 90 }); 91 // 前営業日通知実行日時の記録 92 sheet.getRange(i, 6).setValue(today); // 前日の前営業日実行 93 } 94 } 95} 96 97 98//==================================================================// 99// 関数 // 100// =================================================================// 101 102// 日付が同じかどうかを比較する関数 103function isSameDate(date1, date2) { 104 return ( 105 date1.getFullYear() === date2.getFullYear() && 106 date1.getMonth() === date2.getMonth() && 107 date1.getDate() === date2.getDate() 108 ); 109} 110 111 112// 指定された日付が平日かどうかを判定する関数※1が月曜、5が金曜 113function isWeekday(dateToCheck) { 114 const day = dateToCheck.getDay(); 115 return day >= 1 && day <= 5; 116} 117 118 119// 指定された日付が土曜日または日曜日かどうかを判定する関数※ 0が日曜、6が土曜 120function isWeekend(dateToCheck) { 121 const day = dateToCheck.getDay(); 122 return day === 0 || day === 6; 123} 124 125 126// Googleカレンダーから日本の祝日を取得する 127function getJapaneseHolidays() { 128 const calendarId = 'ja.japanese#holiday@group.v.calendar.google.com'; 129 const now = new Date(); // 現在の日付を取 130 const oneYearFromNow = new Date(now.getFullYear() + 1, 0, 1); 131 const events = CalendarApp.getCalendarById(calendarId).getEvents(now, oneYearFromNow); 132 const holidays = events.map(event => event.getDate()); 133 return holidays; 134} 135function getHolidays() { 136 const now = new Date(); 137 const oneYearFromNow = new Date(now.getFullYear() + 1, 0, 1); 138 // 日本の祝日カレンダーのIDを定義 139 const holidayCalendarId = 'ja.japanese#holiday@group.v.calendar.google.com'; 140 const calendar = CalendarApp.getCalendarById(holidayCalendarId); 141 const holidays = calendar.getEvents(now, oneYearFromNow); 142 143 const holidayDates = []; 144 145 // 各イベント(祝日)について繰り返し処理 146 for (const holiday of holidays) { 147 // 祝日の日付を取得し、日本のタイムゾーンで"yyyy/MM/dd"形式にフォーマット 148 const holidayDate = Utilities.formatDate(holiday.getStartTime(), Session.getScriptTimeZone(), "yyyy/MM/dd"); 149 // 祝日の日付を配列に追加 150 holidayDates.push(holidayDate); 151 } 152 153 return holidayDates; // 祝日の日付を含む配列を返す 154} 155 156function isHoliday(dateToCheck) { 157 const holidays = getHolidays(); // 祝日のリストを取得 158 159 // 指定された日付が祝日のリストに含まれているかどうかを確認 160 const formattedDateToCheck = Utilities.formatDate(dateToCheck, Session.getScriptTimeZone(), "yyyy/MM/dd"); 161 return holidays.includes(formattedDateToCheck); 162} 163 164// 指定された日付の前の平日(土日を除く前の日)を取得する関数 165function getPreviousWeekday(triggerDate) { 166 const previousDate = new Date(triggerDate); 167 168 do { 169 previousDate.setDate(previousDate.getDate() - 1); 170 } while (!isWeekday(previousDate) || isHoliday(previousDate)); 171 172 return previousDate; 173} 174 175

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

祝日の条件分岐のコードを追加いたしました。
その際にソースコード変更したため当日実行がされなくなってしまいました。

また以前実行したものを再実行してしまうようになってしまいました。
↓こちらのコードで対応しています
const reservationDate = new Date(triggerDate);
Logger.log("トリガー日: " + reservationDate.toLocaleString("ja-JP"));

// 実行日がトリガー日の前営業日より前の日付やトリガー日より後の日付には通知しない const prevWeekday = getPreviousWeekday(today); Logger.log("前営業日: " + prevWeekday.toLocaleString("ja-JP")); if (today < prevWeekday || today >= reservationDate) { continue; } // 通知トリガーが実行日の4日前より未来の場合は通知しない const fourDaysBeforeTrigger = new Date(reservationDate); fourDaysBeforeTrigger.setDate(reservationDate.getDate() - 4); Logger.log("通知トリガーの4日前: " + fourDaysBeforeTrigger.toLocaleString("ja-JP")); if (today < fourDaysBeforeTrigger) { continue; }

補足

Apps Script

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

コードを全てチェックしたわけではありませんが、
気になる部分が2箇所あります。

1) 日付の大小比較で当日を含めて比較をしています

js

1 // 実行日がトリガー日の前営業日より前の日付やトリガー日より後の日付には通知しない 2 const prevWeekday = getPreviousWeekday(today); 3 Logger.log("前営業日: " + prevWeekday.toLocaleString("ja-JP")); 4 if (today < prevWeekday || today >= reservationDate) { 5 continue; 6 }

の部分で、
「トリガー日より後の日付」とコメントに書いてあるのですが、
実際のコードが today >= reservationDate になっているので
同じ日付の場合であっても当日の予定は通知されなくなってしまいます。
ここは > で比較すべきです。

2) 実行日が時刻を含んだままになっています

js

1 // 現在の日付と時刻を取得 2 const today = new Date(); 3 Logger.log("今日: " + today.toLocaleString("ja-JP"));

ここのログを見ると時刻が含まれていることがわかると思います。
シートに入力した実行予定日には時刻は入力されていないと思いますので、
午前零時になっています。
これも

js

1 const reservationDate = new Date(triggerDate); 2 Logger.log("トリガー日: " + reservationDate.toLocaleString("ja-JP"));

のログで確認できます。
このことにより、>= や > 等で日付の大小比較をすると
実行日の方が実行予定日より時刻の分だけ大きいと判断されてしまいます。

実行日と実行予定日を日付のみで比較できるように

js

1 // 現在の日付と時刻を取得 2 const today = new Date(); 3 today.setHours(0, 0, 0, 0); // 時刻を午前零時にする 4 Logger.log("今日: " + today.toLocaleString("ja-JP"));

このように、時刻部分を午前零時にしておきます。

ちなみに、時刻を全て午前零時で統一しておくと
等しいかどうかの比較はisSameDate関数を使わなくても

js

1if (today.getTime() === reservationDate.getTime()) { 2 ... 3}

のように日付オブジェクトのメソッド1つで比較できるようになります。

投稿2024/04/26 08:44

編集2024/04/26 08:49
codemaker

総合スコア89

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

codemaker

2024/04/26 09:06

ご希望どおりの結果になるかどうかはわかりませんが、 少なくとも実行日と実行予定日が同じときに 日付の比較でcontinueされなくなるので、 // 当日通知実行 if ( のif文には到達すると思います。 それ以上は確認していません。
Rin22209853

2024/04/26 10:14

ありがとうございました。 ご回答いただきました箇所を修正したところ正常に動きました。 コードを書いているうちに訳が分からなくなってきてしまっていたので、大変助かりました。 丁寧にご説明いただき誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問