前回のおさらい
前回はカレンダーに追加した定例会議の予定の説明欄に、複製した議事録テンプレートのURLをつけていきました。
楽をするためにとても頑張っておりまして、いよいよ複雑さが増してまいりました。
カレンダーの予定にファイルをつけ、余分な議事録を削除する処理はこのように実現しました。
- 定例会議の議事録テンプレートを取得する
- 定例会議の日時を取得する
- 日時をyyyymmdd形式にする
- 議事録テンプレートをコピーする
- コピーした議事録テンプレートの名前を「yyyymmdd_定例会議議事録」に変更する
- 定例会議の説明に議事録のURLをはりつける
- 祝日の議事録を削除する
上の処理をコードにするとこのようになりました。
// 議事録テンプレートを取得
const minutesTemplateName = 'yyyymmdd_定例会議議事録'
const minutesTemplate = DriveApp.getFilesByName(minutesTemplateName).next()
// 議事録テンプレートをコピーして名前を変更し、定例会議の説明にURLを貼り付ける
regularMeetings.forEach(regularMeeting => {
// 議事録の名前のyyyymmddにあたる部分を定義
const regularMeetingDate = new Date(regularMeeting.getStartTime())
const yyyy = regularMeetingDate.getFullYear()
const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
const yyyymmdd = yyyy + mm + dd
// 議事録のコピーを作成し、議事録の名前を変更
const regularMeetingMinutes = minutesTemplate.makeCopy()
regularMeetingMinutes.setName(`${yyyymmdd}_定例会議議事録`)
// コピーした議事録のURLを定例会議の説明部分にはりつけ
regularMeeting.setDescription(regularMeetingMinutes.getUrl())
})
// 祝日に設定された定例会議と祝日の議事録を削除
targets.forEach(target => {
if (target) {
const targetDate = target.getStartTime()
const targetYyyy = targetDate.getFullYear()
const targetMm = (targetDate.getMonth() + 1).toString().padStart(2, '0')
const targetDd = (targetDate.getDate()).toString().padStart(2, '0')
const targetYyyyMmDd = targetYyyy + targetMm + targetDd
const targetMinutesName = `${targetYyyyMmDd}_定例会議議事録`
const targetMinutes = DriveApp.getFilesByName(targetMinutesName).next()
targetMinutes.setTrashed(true)
target.deleteEvent()
}
})
コード全体
const setRegularMeeting = () => {
// 翌月末を取得
const current = new Date()
const nextMonthLastDay = new Date(current.getFullYear(), current.getMonth() + 2, 0)
// 繰り返しルールを設定
const calendarApp = CalendarApp
const recurrence = calendarApp.newRecurrence()
const weeklyRecurrenceRule = recurrence.addWeeklyRule()
const recurrenceRule = weeklyRecurrenceRule.onlyOnWeekday(calendarApp.Weekday.MONDAY).until(nextMonthLastDay)
// 定例会議の名称、当月最初の定例会議の開始日時、終了日時を設定
const title = '定例会議ですよ!'
const dayIndex = new Date(current.getFullYear(), current.getMonth() + 1, 1).getDay()
const firstDay = (9 - dayIndex) % 7 === 0 ? 7 : (9 - dayIndex) % 7
const startTime = new Date(current.getFullYear(), current.getMonth() + 1, firstDay, 10, 0, 0)
const endTime = new Date(current.getFullYear(), current.getMonth() + 1, firstDay, 11, 0, 0)
// 予定の繰り返しを作成
const defaultCalendar = calendarApp.getDefaultCalendar()
const eventSeries = defaultCalendar.createEventSeries(title, startTime, endTime, recurrenceRule)
// 翌月の祝日を取得
const holidayCalendar = calendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com')
const nextMonthFirstDay = new Date(current.getFullYear(), current.getMonth() + 1, 1)
const holidays = holidayCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)
// 定例会議を取得
const events = defaultCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)
const regularMeetings = events.reduce((accum, event) => {
if (event.getTitle() === title) accum.push(event)
return accum
}, [])
if (regularMeetings.length === 0) return
// 祝日に設定された定例会議を取得
// 祝日に定例会議が設定されていない場合はundefinedを取得
const targets = holidays.map(holiday => {
return regularMeetings.find(regularMeeting => {
return regularMeeting.getStartTime().getDate() === holiday.getStartTime().getDate()
})
})
// 議事録テンプレートを取得
const minutesTemplateName = 'yyyymmdd_定例会議議事録'
const minutesTemplate = DriveApp.getFilesByName(minutesTemplateName).next()
// 議事録テンプレートをコピーして名前を変更し、定例会議の説明にURLを貼り付ける
regularMeetings.forEach(regularMeeting => {
// 議事録の名前のyyyymmddにあたる部分を定義
const regularMeetingDate = new Date(regularMeeting.getStartTime())
const yyyy = regularMeetingDate.getFullYear()
const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
const yyyymmdd = yyyy + mm + dd
// 議事録のコピーを作成し、議事録の名前を変更
const regularMeetingMinutes = minutesTemplate.makeCopy()
regularMeetingMinutes.setName(`${yyyymmdd}_定例会議議事録`)
// コピーした議事録のURLを定例会議の説明部分にはりつけ
regularMeeting.setDescription(regularMeetingMinutes.getUrl())
})
// 祝日に設定された定例会議と祝日の議事録を削除
targets.forEach(target => {
if (target) {
const targetDate = target.getStartTime()
const targetYyyy = targetDate.getFullYear()
const targetMm = (targetDate.getMonth() + 1).toString().padStart(2, '0')
const targetDd = (targetDate.getDate()).toString().padStart(2, '0')
const targetYyyyMmDd = targetYyyy + targetMm + targetDd
const targetMinutesName = `${targetYyyyMmDd}_定例会議議事録`
const targetMinutes = DriveApp.getFilesByName(targetMinutesName).next()
targetMinutes.setTrashed(true)
target.deleteEvent()
}
})
}
全体として実現したいこと
- カレンダーへの追加は毎月1日に翌月分を入れる 済
- 定例会議は毎週月曜日の10時から行われる 済
- 祝日は定例会議の予定が入らない 済
- 開始時刻の1時間前にチャットに定例会議の予定が通知される
- 予定に定型の議事録をくっつける 済
- 毎週金曜日に翌週の定例会議の議題を議事録に記入するよう参加者に通知する
今回はGoogleドキュメントの中身に日時を入れていきます。
ドキュメントの文字列を置換する
ドキュメントの文字列を置換していきます。置換するためにはボディクラスのreplaceTextメソッドを使います。
順番に書いていきます。
ドキュメントを取得する
regularMeetings.forEachの中に下のようなコードを入れます。
この文の中で議事録テンプレートから定例会議の議事録をコピーして作成していますので、作成したタイミングでドキュメントの中身も変更します。
const regularMeetingMinutesId = regularMeetingMinutes.getId()
const documentApp = DocumentApp
const regularMeetingMinutesDocument = documentApp.openById(regularMeetingMinutesId)
ドキュメントのボディを取得する
取得したドキュメントからボディを取り出します。ボディとは本文にあたる部分です。
const regularMeetingMinutesBody = regularMeetingMinutesDocument.getBody()
ここで取得したドキュメント(定例会議の議事録)の本文はこのようになっています。

置換したい文字列は下の7つです。
- yyyy
- mm
- dd
- hs
- ms
- he
- me
このうち、yyyy、mm、ddはすでに取得しています。
新たにhs、ms、he、meにあたる文字列を取得していきます。(hsとmsは開始時刻、heとmeは終了時刻)
開始時刻、終了時刻を取得する
const hs = regularMeeting.getStartTime().getHours().toString().padStart(2, '0')
const he = regularMeeting.getEndTime().getHours().toString().padStart(2, '0')
const ms = regularMeeting.getStartTime().getMinutes().toString().padStart(2, '0')
const me = regularMeeting.getEndTime().getMinutes().toString().padStart(2, '0')
開始時刻、終了時刻、あわせて4つの文字列を取得しました。
ドキュメントの文字列を置換する
Googleドキュメントの文字列を置換するにはreplaceTextメソッドを使います。
regularMeetingMinutesBody.replaceText('yyyy', yyyy)
regularMeetingMinutesBody.replaceText('mm', mm)
regularMeetingMinutesBody.replaceText('dd', dd)
regularMeetingMinutesBody.replaceText('hs', hs)
regularMeetingMinutesBody.replaceText('he', he)
regularMeetingMinutesBody.replaceText('ms', ms)
regularMeetingMinutesBody.replaceText('me', me)
置換を実現している部分全体
上では細切れにコードを説明していきました。置換を実現するコードの全体像はこのようになります。
const regularMeetingMinutesId = regularMeetingMinutes.getId()
const documentApp = DocumentApp
const regularMeetingMinutesDocument = documentApp.openById(regularMeetingMinutesId)
const regularMeetingMinutesBody = regularMeetingMinutesDocument.getBody()
const hs = regularMeeting.getStartTime().getHours().toString().padStart(2, '0')
const he = regularMeeting.getEndTime().getHours().toString().padStart(2, '0')
const ms = regularMeeting.getStartTime().getMinutes().toString().padStart(2, '0')
const me = regularMeeting.getEndTime().getMinutes().toString().padStart(2, '0')
regularMeetingMinutesBody.replaceText('yyyy', yyyy)
regularMeetingMinutesBody.replaceText('mm', mm)
regularMeetingMinutesBody.replaceText('dd', dd)
regularMeetingMinutesBody.replaceText('hs', hs)
regularMeetingMinutesBody.replaceText('he', he)
regularMeetingMinutesBody.replaceText('ms', ms)
regularMeetingMinutesBody.replaceText('me', me)
手順は5つです。
- ドキュメントのIDを取得する
- ドキュメントを取得する
- ドキュメントの本文を取得する
- 置換文字列を取得する
- 置換する
ややこしく見えますが、やってることはこれだけです。
ここまでのコード全体
const setRegularMeeting = () => {
// 翌月末を取得
const current = new Date()
const nextMonthLastDay = new Date(current.getFullYear(), current.getMonth() + 2, 0)
// 繰り返しルールを設定
const calendarApp = CalendarApp
const recurrence = calendarApp.newRecurrence()
const weeklyRecurrenceRule = recurrence.addWeeklyRule()
const recurrenceRule = weeklyRecurrenceRule.onlyOnWeekday(calendarApp.Weekday.MONDAY).until(nextMonthLastDay)
// 定例会議の名称、当月最初の定例会議の開始日時、終了日時を設定
const title = '定例会議ですよ!'
const dayIndex = new Date(current.getFullYear(), current.getMonth() + 1, 1).getDay()
const firstDay = (9 - dayIndex) % 7 === 0 ? 7 : (9 - dayIndex) % 7
const startTime = new Date(current.getFullYear(), current.getMonth() + 1, firstDay, 10, 0, 0)
const endTime = new Date(current.getFullYear(), current.getMonth() + 1, firstDay, 11, 0, 0)
// 予定の繰り返しを作成
const defaultCalendar = calendarApp.getDefaultCalendar()
const eventSeries = defaultCalendar.createEventSeries(title, startTime, endTime, recurrenceRule)
// 翌月の祝日を取得
const holidayCalendar = calendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com')
const nextMonthFirstDay = new Date(current.getFullYear(), current.getMonth() + 1, 1)
const holidays = holidayCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)
// 定例会議を取得
const events = defaultCalendar.getEvents(nextMonthFirstDay, nextMonthLastDay)
const regularMeetings = events.reduce((accum, event) => {
if (event.getTitle() === title) accum.push(event)
return accum
}, [])
if (regularMeetings.length === 0) return
// 祝日に設定された定例会議を取得
// 祝日に定例会議が設定されていない場合はundefinedを取得
const targets = holidays.map(holiday => {
return regularMeetings.find(regularMeeting => {
return regularMeeting.getStartTime().getDate() === holiday.getStartTime().getDate()
})
})
// 議事録テンプレートを取得
const minutesTemplateName = 'yyyymmdd_定例会議議事録'
const minutesTemplate = DriveApp.getFilesByName(minutesTemplateName).next()
// 議事録テンプレートをコピーして名前を変更し、定例会議の説明にURLを貼り付ける
regularMeetings.forEach(regularMeeting => {
// 議事録の名前のyyyymmddにあたる部分を定義
const regularMeetingDate = new Date(regularMeeting.getStartTime())
const yyyy = regularMeetingDate.getFullYear()
const mm = (regularMeetingDate.getMonth() + 1).toString().padStart(2, '0')
const dd = regularMeetingDate.getDate().toString().padStart(2, '0')
const yyyymmdd = yyyy + mm + dd
// 議事録のコピーを作成し、議事録の名前を変更
const regularMeetingMinutes = minutesTemplate.makeCopy()
regularMeetingMinutes.setName(`${yyyymmdd}_定例会議議事録`)
// コピーした議事録のURLを定例会議の説明部分にはりつけ
regularMeeting.setDescription(regularMeetingMinutes.getUrl())
// 議事録本文の日時を修正
const regularMeetingMinutesId = regularMeetingMinutes.getId()
const documentApp = DocumentApp
const regularMeetingMinutesDocument = documentApp.openById(regularMeetingMinutesId)
const regularMeetingMinutesBody = regularMeetingMinutesDocument.getBody()
const hs = regularMeeting.getStartTime().getHours().toString().padStart(2, '0')
const he = regularMeeting.getEndTime().getHours().toString().padStart(2, '0')
const ms = regularMeeting.getStartTime().getMinutes().toString().padStart(2, '0')
const me = regularMeeting.getEndTime().getMinutes().toString().padStart(2, '0')
regularMeetingMinutesBody.replaceText('yyyy', yyyy)
regularMeetingMinutesBody.replaceText('mm', mm)
regularMeetingMinutesBody.replaceText('dd', dd)
regularMeetingMinutesBody.replaceText('hs', hs)
regularMeetingMinutesBody.replaceText('he', he)
regularMeetingMinutesBody.replaceText('ms', ms)
regularMeetingMinutesBody.replaceText('me', me)
})
// 祝日に設定された定例会議と祝日の議事録を削除
targets.forEach(target => {
if (target) {
const targetDate = target.getStartTime()
const targetYyyy = targetDate.getFullYear()
const targetMm = (targetDate.getMonth() + 1).toString().padStart(2, '0')
const targetDd = (targetDate.getDate()).toString().padStart(2, '0')
const targetYyyyMmDd = targetYyyy + targetMm + targetDd
const targetMinutesName = `${targetYyyyMmDd}_定例会議議事録`
const targetMinutes = DriveApp.getFilesByName(targetMinutesName).next()
targetMinutes.setTrashed(true)
target.deleteEvent()
}
})
}
実行結果
カレンダー

Googleドライブ

Googleドキュメント

カレンダーに定例会議の予定が入りましたし、議事録はつくれましたし、議事録の本文の修正もできました。
おや、参加者と場所が空白ですね。
次は、参加者と場所を自動的に入れます。
自動化って面倒くさいですね。
修羅場★穴場
