7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

自動で議事録を準備する

Last updated at Posted at 2020-07-13

概要

手動で議事録の準備をするのが面倒で、自動でやりたかったので新人でも、楽がしたい! ~議事録の準備~を参考にして作ったが、要件が若干違っていた。

要件

  • カレンダーから予定を見て、会議が始まる前に議事録の準備ができている
  • SLACKにて通知する
  • 会議がない場合は作成をしない
  • 議事録ファイルは毎回作ってはいけない
  • 議事録のファイルは同じファイルを利用する
  • そのファイルの「以下議事録」の下からテンプレートを挿入する

最後の要件が意外と難しく、全て出来なかった。
議事録ファイルから「以下、議事録」を探して位置を見つけて、そこからテンプレートを挿入する。

以下議事録.png

ロジック

ほとんどが新人でも、楽がしたい! ~議事録の準備~と同じ。

  • 1時間毎にGASバッチが起動
  • カレンダーを見る。
  • 朝会の予定が入っている場合
  • 議事録ドキュメントの「以下、議事録」を見つける
  • その位置にテンプレートから挿入する。
  • SLACKに通知する

各ファイルの役割

  • 朝会議事録テンプレートファイル

  • GASスクリプト本体

  • 挿入するテンプレート

  • 議事録ファイルID

  • 議事録のファイルID

  • 議事録が変わる場合、こちらも変更する

  • 議事録ID.png

  • 自動議事録作成会議名

  • カレンダーに登録している会議名

  • カレンダーに登録している会議名が変わったら、こちらも合わせて変更する
    会議名.png

Webhookを発行

slack apiからwebhookを発行する。

GAS

8割が新人でも、楽がしたい! ~議事録の準備~です。本当にありがとうございます。

コード.gs
// Googleカレンダーに登録された「直近1時間分」のイベントの配列を返す
function getCalendarEventList() {

  var now = new Date();
  var oneHourFromNow = new Date(now.getTime() + (1 * 60 * 60 * 1000));
  var events = CalendarApp.getDefaultCalendar().getEvents(now, oneHourFromNow);

  var eventList = [];
  for (i = 0; i < events.length; i++) {
    eventList.push(events[i]);
  }

  return eventList;  
}
// スプレッドシートに記載された文字の配列を返す
function getEventTitleFromSpredsheet(spredsheetpath) {

  var ss = SpreadsheetApp.openById(spredsheetpath);
  var sheet = ss.getSheets()[0];
  var range = sheet.getRange(1, 1, ss.getLastRow());
  var values = range.getValues();

  var meetingList = [];
  for (i = 0; i < values.length; i++) {
    value = values[i];
    meetingList.push(value[0]);
  }

  // スプレッドシートに記載された文字の配列を返す
  return meetingList;
}
function start(){

  // 議事録自動準備プログラムで、最初に起動する関数
  // 「直近のイベント一覧」と「議事録を作成するMTG名」を取得し、一致する名前があれば議事録を準備する
  var SPREDSHEETPATH = "[自動議事録作成会議名のID]"
  var meethingList = getEventTitleFromSpredsheet(SPREDSHEETPATH);
  var eventList = getCalendarEventList();

  var createFlag = false;
  var meethingName = '';

  for (var i = 0; i < meethingList.length; i++) {
    for (var j = 0; j < eventList.length; j++) {
      if (meethingList[i] == eventList[j].getTitle()) {

        var functionTriggeredTime = new Date();
        var meethingStartTime = eventList[j].getStartTime();

        // 会議の開始時間が、プログラム起動時間より後ならば、createFlag=trueにする。
        // 会議中にプログラムが起動した場合には、議事録は作成されない。
        if (functionTriggeredTime < meethingStartTime){
          createFlag = true;
          meethingName = eventList[j].getTitle();
        }
      }
    }
  }

  if (createFlag) {
    prepareMinutes(meethingName);
    Logger.log('議事録を準備しました。');
    Logger.log('MTG名:' + meethingName);
  } else {
    Logger.log('議事録は準備されませんでした。');
  }
}
//議事録を作成し、slackに通知する
function prepareMinutes(meethingName){

  // slack Incoming Webhook
  var POSTURL = '[WebhookのURL]';

  var fileUrl = insertTemplate();
  
  // slackに議事録へのurlを投稿する
  var message = '議事録を準備しました。' + '\n\n' + '会議名:' + meethingName + '\n' + 'URL : ' + fileUrl;
  var jsonData = {
    'text':message
  };
  var payload = JSON.stringify(jsonData);
  var options = {
    'method':'post',
    'contentType':'application/json',
    'payload':payload
  };
  
  UrlFetchApp.fetch(POSTURL, options);
}

// 「以下議事録」の文字を探す
function getHorizontal(mininutes_doc) {
  var LIMIT = 50; // もしも見つからなかった場合用
  var listItems = mininutes_doc.getBody().getParagraphs();
  for (var i = 0; i < LIMIT; i++) {
    var value = listItems[i].getText();
    if (value.indexOf("以下、議事録") != -1) {
      return i + 1;
    }
  }
  return i;
}

// 議事録ファイルにテンプレートを入れる
function insertTemplate() {
  // 議事録ファイルのIDが入っているスプレッドシートから取得
  var MININUTES_DOC_ID = '[議事録ファイルのID]'
  var mininute_ids = getEventTitleFromSpredsheet(MININUTES_DOC_ID);
  var mininute_id = mininute_ids[0]
  var template_doc = DocumentApp.getActiveDocument();
  var mininutes_doc = DocumentApp.openById(mininute_id);

  var horizontal = getHorizontal(mininutes_doc);
  mininutes_doc.getBody().insertHorizontalRule(horizontal);
  horizontal++;
  template_doc.getBody().getParagraphs().forEach(function(value, i) {
    if (value.getType() == DocumentApp.ElementType.LIST_ITEM) {  // 箇条書きの場合
      mininutes_doc.getBody().insertListItem(horizontal + i, value.getText()).setGlyphType(DocumentApp.GlyphType.BULLET);
    } else {
      mininutes_doc.getBody().insertParagraph(horizontal + i, value.getText());
      mininutes_doc.getBody().getParagraphs()[horizontal + i].setAttributes(value.getAttributes()); // 見出しなどの属性を設定
    }
  })
  return mininutes_doc.getUrl();
}

出来たら動かして、動くことを確認する。

トリガを設定する

1時間毎に動かすトリガを設定します。時計のアイコンから設定が出来ます。

トリガ.png

1時間毎に動くように設定します。

1時間毎にトリガ.png

一応、これで完成です。

参考サイト

Google Apps Scriptで議事録テンプレ作成を楽にした
GASを利用した定例議事録作成の自動化
新人でも、楽がしたい! ~議事録の準備~
ありがとうございます。

課題

  • テンプレートからコピーする際、以下が出来ない
  • 箇条書きがネストしている場合
  • 水平線がコピーできない
  • 他にもたくさんコピーできないパターンはある
  • 担当者が離れてこのスクリプトを止めなかった場合、延々とスクリプトが動いてしまい、ドキュメントが肥大化してしまう
  • 自動的の止まる仕組みを入れたいよね〜
  • GASの場合、設定ファイル的なものってどこに配置するのが適切なんだろう?
  • 「以下、議事録」の設定も、外出にするべきなんだろうか。
7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?