🛠️

Google Workspace Studioでワークフローを作ったけど結局ほぼGASになった話

に公開

今北産業

  • Google Workspace Studio を使って社内ワークフローを作り、記事も書いた
  • Google Workspace Studio の Actions 抹消事件が発生する
  • 消えた Actions を GAS で穴埋めしたため記事を書き換え投稿する ← イマココ

はじめに

全社で生成 AI を活用するために Google Workspace へ移行した話で紹介されている通り、
社内のグループウェアを Google Workspace に移行しました。
この記事では、申請に使用するワークフローを Google Workspace Studio と他 Workspace ツールを使用して作成した際の内容をまとめています ✍🏻


という内容で記事を作成したのですが、度重なる Studio Actions の削除により、それまでのフローステップが動かなくなってしまいました。(⌒▽⌒) <やってくれるやないの
その Step を GAS に置き換えたため、この記事もそのように修正して投稿している次第です。

この記事では、作ったものの紹介と Studio の使用感等記載しますので、諸々参考になれば幸いです。
※Studio の全 Actions 等を網羅した内容ではないのでご了承ください。

消えた機能ざっくり時系列
  1. 11 月後半に Flows(アルファ版) で仮作成する
  2. 12/4 GA となり Studio に名称変更とともにチャットスペースへの送信や Webhook といった Actions が削除される
  3. 12/12 GA 後も追加可能だったカスタム Webhook も使用できなくなる

使用ツールと用途

  • Google Forms:申請情報の入力フォームとして使用
  • Google Workspace Studio:シートへの申請行追加で使用
  • Google Apps Script:メール/チャット送信のスクリプトで使用
  • Google Chat:申請の通知とコミュニケーションに使用
  • Google Sheets:申請データの集計と管理に使用

申請ワークフローの構成概要

今回作成したワークフローの全体像は以下の通りです。

※図中の GAS 部分は、Studio の機能削除により代替実装した部分です

  1. 申請者が Google Forms で申請内容を入力し送信
  2. フォームの申請をトリガーに、申請内容を承認者にメール送信
  3. Studio が起動し、申請内容を集計用 Google Sheets の指定シートに行追加
  4. 指定した申請用 Chat スペースに申請内容を含めたメッセージを GAS が投稿
  5. 承認者がメッセージに対してスタンプで承認 or 却下を選択
  6. Studio の承認ワークフローが起動し、承認 or 却下の内容に応じて Google Sheets の該当申請行ステータスを更新
  7. 申請者に結果を Chat メッセージで通知

当初は、図の ③ フォーム回答内容のメール送信はなく、⑦ チャット送信は Studio に「スペースにチャットを送信する」カスタム Webhook を追加して動かしていたのですが、

  • 「We are at capacity, we'll be back soon」という AI エージェントエラー
  • Actions でのカスタム Webhook 使用停止による Step 削除エラー

により、Studio の稼働が安定しない・動かない=申請したが集計用シートやチャットといった場合、記録が出来ないことによるヌケモレの可能性が出ました。
そのため、Studio 外で申請内容を承認者にメールを送信する GAS、使用できなくなった Webhook の代わりにチャット送信の GAS をそれぞれ追加しました。


このワークフローは大きく分けて申請フェーズ承認フェーズの2つに分かれており、それぞれで Studio でフローを作成しています。
申請から承認に繋げるフックとして使用したいものが Actions になく、Starter にあったため分ける形となりました。
自分は主に申請フェーズの構築を担当、承認フェーズは他メンバーに担当いただき、分担して作成しました。
※なので以降は申請フェーズでの内容にフォーカスしてます!

申請フォームについて

Google Forms で申請フォームを作成しました。
以前使用していた Lark というグループウェアでの申請フォームを参考に、必要な項目を作成しています。
以前は申請種別ごとにフォームが分かれていたのですが、今回は一つのフォームにまとめ、選択した申請種別によりその後の入力項目が切り替わるよう作成しています。

フォームには別途用意したスプシをリンクし、メール送信用 GAS 用シートと Studio 用シート(申請ごと)を作成しています。

Studio フローの全体像

Studio の処理全体像はこちらです。

Starter

When a form response comes in を Starter とし、作成した申請フォームを設定することで、フォームの回答があった際にワークフローが起動するようにしています。

Actions - Check if

Check if actions を使用してフォームの申請種別で分岐し、申請内容を追加するシート指定や、Chat スペースに送信するメッセージ内容を切り替えています。

今回は単純な分岐にしていますが、ANDOR条件も使用可能なので、様々な条件分岐も実現できそうです。
ここでは一致条件の値は固定値を指定していますが、前 Step に値取得や Extract などの操作がある場合は +Variablesから動的な値も使用可能です。

Actions - Add a row

Add a row actions を使用して、フォームの回答内容を集計用 Google Sheets の指定シートに行追加しています。

今回はひとつのスプシの中で 2 つの申請をシートで分けて管理しているので、Sheet には Check if で一致する申請のものを指定しています。
Add data by columnで追加する行の内容は、列に対応するフォームの回答項目を取得するようにします。(+Variables からフォームの回答項目を選択可能です。)
申請番号は、シート内で自動連番になるようにしたかったのでここでは空欄にし、シート内の該当セルに関数を追加しています。
※スプシ関数素人なので、ファイル内で Gemini さんに質問したら秒で設定してくれました。

// 例:資格取得申請シートのA列が申請番号列の場合
// 2行目以降に申請内容が追加されたら自動連番を振る関数
=ARRAYFORMULA( IF(ROW(A:A)=1, "資格取得支援制度申請番号", IF(ISBLANK(B:B), "", ROW(A:A)-1)))

GAS について

申請集計用として作成したスプシのメニューから、「拡張機能」→「Apps Script」で追加しています。
詳細は割愛しますが、メール送信用とチャット送信用でコードファイルを作成し、メールはフォームの回答があったら、チャットはシートに行が追加されたら、をトリガーとして動くようにしました。

チャット送信については、新規で行が追加されたときだけメッセージを送信するように制御しています。
具体的には、下記のような感じです。

  1. トリガーは「変更時」に設定
    • Studio による行追加を検知するため、トリガーは「編集時」ではなく 「変更時」 に設定
  2. 行操作の判定処理
    • Studio が行追加または既存行を更新したのかを判定してからメッセージ送信処理に移ります
GAS コード(一部)
// =======================================================
// 1. 行操作の判定
// =======================================================
console.log(`[${sheetName}] 変更検知。新規行か判定するために待機します...`);
Utilities.sleep(3000);
SpreadsheetApp.flush();

// =======================================================
// 2. 「現在の最終行」と「前回通知済みの行」を比較する
// =======================================================

// 現在データが入っている本当の最終行を取得
const currentLastRow = getLastRowInColumn(sheet, 2); // B列基準

// まだデータがないなら終了
if (currentLastRow < 1) return;

// 保存されている「前回通知した行番号」を読み込む
// プロパティキー例: 'LAST_NOTIFIED_ROW_書籍購入申請'
const propKey = "LAST_NOTIFIED_ROW_" + sheetName;
const scriptProperties = PropertiesService.getScriptProperties();
const savedRowString = scriptProperties.getProperty(propKey);

// 初回実行時などで値がない場合は 0 とする
let lastNotifiedRow = savedRowString ? parseInt(savedRowString, 10) : 0;

console.log(`現在の行: ${currentLastRow} / 前回通知した行: ${lastNotifiedRow}`);

// 現在の行が、前回より増えていなければ「既存行の更新」とみなして終了
if (currentLastRow <= lastNotifiedRow) {
  console.log("既存行の更新、または行削除のため通知しません。");

  // もし行削除で行数が減っていた場合、次回の追加に備えて記録を同期しておく
  if (currentLastRow < lastNotifiedRow) {
    scriptProperties.setProperty(propKey, currentLastRow.toString());
    console.log("行数が減ったため、記録を現在の行数に同期しました。");
  }
  return;
}

// =======================================================
// 3. 新規行と確定したので、データが完全になるのを待つ
// =======================================================
const MAX_RETRIES = 30;
let values = [];
let dataFound = false;

for (let i = 0; i < MAX_RETRIES; i++) {
  SpreadsheetApp.flush();

  // 改めて行を取得(待機中に行が変わることは稀だが念のため)
  const checkRow = getLastRowInColumn(sheet, 2);

  // 行が減ってしまったら中断
  if (checkRow < currentLastRow) {
    console.log("待機中に行が削除されました。");
    return;
  }

  const lastColumn = sheet.getLastColumn();
  values = sheet.getRange(currentLastRow, 1, 1, lastColumn).getValues()[0];

  // B列(名前)が入っているか確認
  if (values[1] && values[1] !== "") {
    dataFound = true;
    console.log(`データ完了を確認(行: ${currentLastRow}):`, values);
    break;
  }
  Utilities.sleep(1000);
}

if (!dataFound) {
  console.error(
    `タイムアウト:行は追加されましたが、有効なデータ(名前)が書き込まれませんでした。`
  );
  return;
}

以上が申請フェーズの設定になります!
後続の承認フェーズについて詳細は割愛しますが、承認 or 却下のスタンプリアクションにより Studio が起動し、スプシのステータス更新や申請者への通知を行う形となっています。

Studio 詰まったポイント

①Add a row の情報取得

Webhook のペイロードで使っているのはほぼフォーム回答の項目ですが、ひとつだけ Add a row で新規行追加した時に自動採番される申請番号の値を使っています。
後続の承認フェーズで、「リアクションが付いた申請」⇔「申請集計シートの行」を紐づける一意な値として使用するため、Chat メッセージに含める必要があります。
設定中、Add a row の Variables から申請番号列の値を入れてるのに、なぜかメッセージには違う列の値が表示されて混乱しました。(例:申請番号が表示されると思ったら、なぜか受験料の値が出ている的な)

原因は、各申請シートで同じ申請番号という列名を使っていたからでした…。
なんとなく、行追加ではシート指定してるし、行追加後すぐに値取得するから同じ列名でも大丈夫と思っていたのですが、ここは一意にしないとだめでした。◯◯ 申請番号のようにそれぞれ修正してから試すと、ちゃんと申請番号をメッセージに含めることができました。

②Extract の設定

今回は Extract(Gemini にコンテンツを渡すと、内容を分析し、指定した情報を抽出してくれる Actions)は使っていないですが、仮作成した時に使ってて詰まったポイントがありました。
Google Workspace Studio(旧 Google Workspace Flows)で領収書の自動処理を実装してみた
この参照記事にある通り、Extract の Custom content name には英語を使う必要があるので要注意です。
References info from an unavailable step…?(゚ ∀ ゚)と時間使ってしまったので…

③ 通知なく遠慮もなく削除される Actions

こればっかりは今後安定することと、削除された Actions たちが復活することを祈るほかありません…。
急に消えて作った Studio が動かなくなると、何が原因なのかわからなくなるので何かしらの形で教えてくれたらいいのになあとは思います。

Studio の所感

  • シンプルな構成のものは既存の Actions を組み合わせて簡単に作成できる
  • Gemini に指示して作ってもらうこともできるので便利
  • 少し複雑な処理をさせようとすると使いたい Actions がなかったりする
  • 作成したいものによっては Studio ではなく GAS を使った方が早い場面もある
  • 安定するまでは Studio の使用は様子を見たほうがよいかもしれない…

Gemini も使えますし、作りたいもの次第では有用だとは思います。

_人人人人人人人人人人人人人人人_
>  全部 GAS でよいのでは?  <
 ̄ Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y  ̄

と聞こえてきそうですが(自分も思いましたが)、今後本格的に社内運用していく中でなにかあれば都度よりよい構成に変えていきたい所存です!

マーベリックスのテックブログ

Discussion