GAS

GoogleAppsScriptGitHubアシスタント undefinedの解決方法

はじめに

GASとgithubを連携するのに使うChrome拡張機能、
GoogleAppsScriptGitHubアシスタント。便利ですよね。
しかし、そこそこの頻度で以下のようなエラーに遭遇します。
[GitHub assistant] undefined
今回は、このエラーの解決方法について解説していきます。

再認証する

まず1つ目は、再認証する方法です。
Chrome上部ツールバーから拡張機能のボタンを選択、
GitHubアシスタントの・・・からオプションを選択します。
ツールバーのchrome拡張ボタンからオプションを選択

すると、本来はログイン情報に関する画面が表示されます。
が、それと同時に以下のような認証画面がポップアップする場合、
おそらく認証が切れているのでそのままAllowで許可、認証を行ってください。
Googleの認証をAllowする

再ログインする

前項の方法で解決しなかった場合、以下の方法も試してみてください。
前項と同様に上部バーからオプションを開きます。
ログアウトする
ここで一度、ログアウトして再度ログインしてください。
尚、再ログインの際にgithubのアクセストークンが必要になります。

GoogleAppsScriptAPIの設定をオンにする

それでも解決しない場合、
こちらにアクセスして設定をオンにしてください。
GoogleAppsScriptAPIを有効にする

さいごに

Googleアカウントを複数使っていたり、
二段階認証を設定していたりすると、発生頻度が高まるように感じています。
ぜひお役立ていただければと思います。

GASでGoogleDrive上のJSON内容を取得/上書きする方法

はじめに

Drive上にJSONファイルを置いておき、
それをGASから操作する際の基本的な方法を紹介します。

なぜGASでJSONファイルを使うのか

本文の前に、なぜこんなことをするのかについて。
社用webアプリ等に使う半端な量のデータを持つのに有用だからです。

社用とは言えwebアプリケーションですから、
操作すべき何らかの情報や、操作した結果情報を保存する必要があります。
これらをもし全てスプレッドシート上で管理したら、どうなるでしょうか。
web上の操作時にスプレッドシート上の数千行のデータを処理して・・・
となると、20秒とか30秒とかかかります。
webアプリを操作している時の読込みで30秒は流石に酷いですよね。
データ量が本当に多いならBigQueryなどデータベース運用が視野に入りますが、
DB使う程ではないけどシート管理も良くないという時、Drive上にJSONを置いておくのも1つの方法です。

読込のコード

以下のコードでJSONとして扱えます。

const file = DriveApp.getFileById("id");
const jsonStr = file.getBlob().getDataAsString("UTF-8");
const json = JSON.parse(jsonStr);

書き込み(上書き)のコード

以下のコードで書き込みできます。
drive上のテキストファイルの扱いとほぼ同じですね。
上書きとなりますので、差分のみ変更した場合はそのようなデータを予め作成する必要があります。

const file = DriveApp.getFileById("id");
file.setContent(JSON.stringify(json));

最後に

コード自体はごくシンプルですが、扱いようによっては非常に便利です。
いかなる時にドライブ上のJSONデータを運用するのかという点が重要だと思います。
getValuesが遅すぎると悩んでいる方は一度試してみてください。

GASの変更時トリガーが実行頻度によっては実行されない問題について検証してみた

0.はじめに

GASの変更時トリガーってあるけど、
本当に全ての変更をキャッチしてくれてるの?

と疑問に思ったので検証してみました。

例えばGoogleフォーム回答が入力されるスプレッドシートに対して、
シート変更時トリガーを仕込んでおり複数から同時に回答があったとしたら・・・?
本当はそんなスクリプトは組まない方がいいわけですが、
そんな時どうなるのか気になる方はぜひご覧ください。

 

1.検証用のコードとトリガー

本当の限界頻度を知りたいので、ごくシンプルな負荷の低いコードを使いました。


function myFunction() {
  console.log("testやで");
  GmailApp.createDraft("test@test","testやで","testやわ");
}

トリガーはこちら。普通の変更時トリガーです。
変更時トリガー設定の画像
もちろんこのスクリプトはスプレッドシートにバインドしています。

 

2.高速で実行してみた

まず、適当に10回実行してみた結果がこちらです。
セルを10個変更したスプレッドシートの画像
6行の実行ログ
10回変更したのに実行ログは6行しかあらへん。どういうこっちゃ。
実行されてるけど、実行ログだけ出てないのか?と思い、Gmailの下書きを確認します。
6件だけのメール下書き
やっぱり6件しかない。という事で、6回しか実行されていないようです。
高速での変更時トリガーは実行が欠損する
という事が分かりました。
(1回だけでは試行回数不足のため複数回、別時期にも試しております。)

 

3.実行頻度を落として実行してみた

どれぐらいが敷居なのか判断したいため、
秒間1~3回程度に実行ペースを落としてみます。

10回成功 秒間1~2回
10回成功した画像

10回成功 秒間1~3回
10回成功した画像

9回成功 秒間1~2回
9回成功した画像

無暗に高速実行した時より成功率は上がりましたが、まだムラがあります。
(3回だけでは試行回数不足のため複数回、別時期にも試しております。)

 

4.秒間1回にしてみた

では秒間1回ではどうでしょうか。

秒間1回程度で10回成功
秒間1回程度の実行で10回成功している画像

秒間1回程度で10回成功その2
秒間1回程度の実行で10回成功している画像

秒間1回程度で10回成功その3
秒間1回程度の実行で10回成功している画像

概ね、1秒に1回程度の実行であれば安定して動きそうです。
私の過去の検証や経験則を合わせても、
1秒1回以下のペースで安定すると結論付けてよいのでは、と考えています。

 

5.さいごに

いかがでしたでしょうか。
変更時トリガーが安定しない、同時実行の際に不安、
といった場合にはぜひこの内容を思い出してみてください。

それにしても、皆さんに画像で伝わるように秒間1回で実行するとかって難しいですね…w
GASの限界に挑戦するシリーズ、個人的に面白いなと思っているので、
何か思いつけばまたやってみようと思います。
「これ試してみてほしい!」等あればお気軽にコメントくださいね。

フォームのデータを取得できませんでした。に対処したもう1つのシンプルな方法

0.はじめに

以前、こんな記事を書きました。
「フォームのデータを取得できませんでした。のエラーに対処した話」

内容としては、おそらくGoogle側の問題が原因で発生するエラー、
Exception: フォームのデータを取得できませんでした。しばらくしてからもう一度お試しください。
に対処する方法についてでした。
GASのフォーム送信時トリガーで回答情報を取得しようとする際に起こるエラーですね。

ただ、エラーを防止する方法は無く、
エラーが起きても後から再実行できる方法を提案するものでした。

記事の投稿から時間が経ち、私が採用しているコードも変わりました。
相変わらずエラーを防止する方法はありませんが、
上記の記事はいまだに結構アクセスがありますので、
最新版をお届けしようと思います。

 

1.コード

// フォーム送信時トリガー
function main(e) {
  let datas = {status:false, msg:"初期値"};
  //フォームデータ取得を20回トライする
  for(let i = 1; i<=20; i++){
    datas = getFormDatas(e);
    if(datas.status) break;
    Utilities.sleep(3000);
  }
  if(!datas.status){
    //前回記事の方法やエラーメール送信などを書く
  };
}

//フォームデータの取得
function getFormDatas(e){
  try{
    // 必要な処理をする
    const formTitle = FormApp.getActiveForm().getTitle();
    const itemresponses = e.response.getItemResponses();
    const address = e.response.getRespondentEmail();
    const responseId = e.response.getId();
    return {status:true,formTitle:formTitle,itemresponses:itemresponses,address:address,category:category,responseId:responseId};
  }
  catch(er){
    console.log(er);
    return {status:false, msg:er.message};
  }
}

 

2.簡易解説

フォーム回答を取得する部分について、getFormDatasという関数にしています。
これを、成功するまで20回を上限に実行しています。ごくシンプルですね。
高速でループしてもサーバーエラーが解消しない限り無意味なので、
気分的にループ毎に3秒のwaitを入れています。
このwaitに本当に意味があるかは分かりません!

 

3.検証結果

このコードを書いた当時、SeleniumでGoogleフォームを1,500回、回答してテストしました。
※seleniumとは、ブラウザを自動操作するツールです。pythonやC#で使えます。
エラーが出ない時期もありますが、出る時期に行いました。
結果、2回以上のループに入るケースが2割ほどありましたが、
20ループを超えてエラーを吐くケースは1%未満に留まりました。

 

4.ループ回数は何回が適切なのか

ループしすぎて処理がタイムアウトにならなければいいのではないでしょうか。
あまり気にせず適当に決めていいと思いますが、
エラーが出る時期は5ループとか10ループだと結構突破してくる印象があります。

 

5.最後に

いかがでしたでしょうか。
単に試行回数を増やすという強引な対処ですが、
ランダム性のあるこの手のエラーには1つの有効な手立てとなります。
ぜひ、前回記事と合わせて活用してみてください。

AdminDirectoryでエイリアスを追加/削除する方法 – GAS

0.はじめに

エイリアスを操作しようと思っていざ調べてみると、
思いのほか情報が少なく、誤情報もあり困ったため、方法を共有します。

 

1.追加する方法


AdminDirectory.Users.Aliases.insert({"alias":"~~~~@~~~~~"}, "userMail@~~~~");
//AdminDirectory.Users.Aliases.insert({"alias":"エイリアスメールアドレス"}, "ユーザのプライマリメールアドレス");

 

2.削除する方法


AdminDirectory.Users.Aliases.remove("userMail@~~~~", "エイリアス@~~~~");
//AdminDirectory.Users.Aliases.remove("ユーザのプライマリメールアドレス", "エイリアスメールアドレス");

 

3.一括更新できないのか?

色々と検証してみたところ、ループを回すしか方法が無さそうです。
メソッド1発でできる方法があれば是非教えてください。

AdminDirectoryで管理者が自アカウントを変更すると403になる罠

0.はじめに

GoogleAppsScriptでAdminDirectory系のメソッドを実行した際に403が出て若干ハマったので共有します。

 

1.結論

結論:管理者アカウントを操作しようとしたからです。

 

通常、AdminDirectoryServiceはそれなりの権限がないと実行できません。
なぜならユーザー情報やグループ情報を変更したり削除したりできてしまうからです。
「運用中の通常ユーザーの情報は触りたくないから自分のアカウントで試すか~」
とやると、403が返ってきます。
あなたのアカウントは管理者アカウントではありませんか?
AdminユーザーでもAdminユーザーを変更する事ができないようです。


API call to directory.users.aliases.insert failed with error: Not Authorized to access this resource/api

「AdminDirectory 403」等で調べると、スコープや権限の話が出てきますが、
スコープやユーザー変更権限があっても実行できません。
「Groups.list()とかUsers.insert()とかできるのになんで?」となりますが、
管理者アカウントは操作できる対象ではありません。

 

2.ちなみに実行したコードはこちら

ちなみに私が実行したコードはこれでした。
自分のメールエイリアスを設定しようとしたものです。
Users.Aliasesの情報が少なく「何かコードがおかしいんだ」と悩みましたが、
変更対象アカウントを一般ユーザーにしたところ普通に動作しました。


AdminDirectory.Users.Aliases.insert({"alias":"~~~~@~~~~~"}, "userMail@~~~~");
AdminDirectory.Users.Aliases.remove(userKey, "userMail@~~~~");

GASでドライブに日毎、月毎のフォルダを一気に作成する方法【コピペでOK】

0.はじめに

ドライブに日毎のフォルダを一気に作成したい!
ということをGASで実現する記事です。

ちまちま作成している人はパパっとコピペして活用しちゃってください!

1.フォルダ作成のコード

//エディタ左からライブラリday.jsを追加してください。スクリプトIDは下記。
//1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB
function main() {
  //親フォルダを定義
  const rootFolder = DriveApp.getFolderById("親フォルダID");
  let monthFolder = "";

  //開始日と最終日を設定
  let targetDate = dayjs.dayjs("2023/1/1");
  let endDate = dayjs.dayjs("2024/12/31");

  //最終日を超えるまでtargetDateを加算しつつ繰り返し処理
  while(targetDate.isBefore(endDate.add(1,"day"),"day")){
    //日付が1日or月フォルダ未指定の場合はフォルダ作成(root内に当月フォルダが存在するか確認するとよりgood)
    if(targetDate.date()==1 || monthFolder==""){
      //月フォルダを作成
      monthFolder = rootFolder.createFolder(targetDate.format("YYYY/MM"));
    }
    //月フォルダ内に日フォルダを作成
    monthFolder.createFolder(targetDate.format("YYYY/MM/DD"));
    //targetDateに1日加算
    targetDate = targetDate.add(1,"day");
  }
}

コード内にコメントで書いている通り、day.jsを使用するコードになっています。
エディタ左側のライブラリのところから、追加してください。

day.jsのスクリプトIDはこちら
1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB
day.js公式

実行結果としてはこんな感じになります。
↓↓↓

親フォルダ内に月フォルダが並び・・・

生成済みの月フォルダたち

月フォルダの中には日毎のフォルダが並びます。

月フォルダ内に日毎のフォルダが並ぶ画像

尚、注意点ですが、少し処理時間を要します。
なぜなら1つずつフォルダを順次作成していくからです。

上記の例、丸2年分を私の環境で実行すると、2分15秒かかりました。
手で作成するよりは断然速いですが、あまり大量に作成するとタイムアウトしますので
ある程度は区切って処理していくことをオススメします。

2.当日になったらフォルダを削除したい、そんな時のコード

作って使うだけなら楽ですが、管理上そうもいかない場合が多いです。
そんな時、日次で夜実行のトリガーを設定しておいて当日分を削除するようなコードがあれば便利ですよね。
それがこちらです。月末日の場合は月フォルダも削除します。


//本日のフォルダを削除
function deleteTodayFolder(){
  const rootFolder = DriveApp.getFolderById("親フォルダID");
  const today = dayjs.dayjs();
  const monthFolder = rootFolder.getFoldersByName(today.format("YYYY/MM")).next();
  
  if(monthFolder){
    //月フォルダが見つかったら本日フォルダを検索し、見つかったら削除する
    let todayFolder = monthFolder.getFoldersByName(today.format("YYYY/MM/DD")).next();
    if(todayFolder) todayFolder.removeFolder();

    //翌日が1日だったら月末なので、月フォルダも削除する
    if(today.add(1,"day").date()==1) monthFolder.removeFolder();
  }
}

こちらもday.jsを使いますので事前に追加しておいてくださいね。

3.昨日分を削除したい方へのコード

別に当日分を23時~24時のトリガーで日次削除してもいいけど、
できれば厳密に日が変わってから昨日分を削除したい方はこちらをどうぞ。


//昨日のフォルダを削除
function deleteYesterdayFolder(){
  const rootFolder = DriveApp.getFolderById("親フォルダID");
  const yesterday = dayjs.dayjs().subtract(1,"day");
  const monthFolder = rootFolder.getFoldersByName(yesterday.format("YYYY/MM")).next();
  
  if(monthFolder){
    //月フォルダが見つかったら昨日フォルダを検索し、見つかったら削除する
    let yesterdayFolder = monthFolder.getFoldersByName(yesterday.format("YYYY/MM/DD")).next();
    if(yesterdayFolder) yesterdayFolder.removeFolder();

    //本日が1日であれば先月の月フォルダも削除する
    if(dayjs.dayjs().date()==1) monthFolder.removeFolder();
  }
}

こちらもday.jsです。便利ですね。
私が実務で使っているのも、このパターンです。

4.さいごに

いかかでしたでしょうか。結構シンプルなコードだと思いませんか?
これだけのコードですが、手で作業するとなると莫大な時間を要します。

ぜひGASを活用して効率化、高速化を実現してきましょう!
ご活用いただけると幸いです。それではまた。

GASで契約管理! 管理シートを自動参照&通知日に自動メール通知!

0.はじめに

期日管理、自動化していますか?

「期日の〇ヶ月前になったら通知する」等は定型的で非常に自動化しやすい部分です。
その一例としてシート例とコード例をご紹介します。
もし業務の補助になるなら、ぜひチャレンジしてみてください。

1.スプレッドシート

こんな構成にしました。

契約管理更新シートのサンプル画像

E2セルにはこんな関数が入っています。

=ARRAYFORMULA(IF((D2:D=””)+(F2:F=””)+(F2:F=”-“),””,EDATE(D2:D,F2:F*-1)))

IF文でD列が空白orF列が空白orF列が-の場合は空に、
それ以外の場合はD列からF列の月数をマイナスした日付が入ります。

GASを学ぶとつい全てコードでやってしまいそうになりますが、
関数を適切に活用することでコードがシンプルになり可読性や保守性が向上します。

2.コード

//最初にライブラリdayjsを追加しておくこと
//スクリプトIDは1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB
//メイン処理
function alertContractMain() {
  const targets = getAlertTargets();
  if(targets.length == 0) return;

  for(let t of targets){
    let body = getHtmlBody(t);
    alert(t,body);
  }
}

//アラートメール送信
function alert(target,body){
  let mailArgs = {htmlBody : body};
  GmailApp.sendEmail(target.AlertTo, `契約更新通知 ${target.Title}`,"",mailArgs);
}

//HTMLメール本文を作成する
function getHtmlBody(target){
  const body = `下記、契約更新通知の時期となりましたため通知いたします。<br>
                <br>
                対象:${target.Title}<br>
                カテゴリ:${target.Type}<br>
                契約満了日:${dayjs.dayjs(target.EndDate).format("YYYY/MM/DD")}<br>
                自動更新有無:${target.AutoUpdate}<br>
                コメント:${target.Detail}<br>
                <br>
                必要に応じて更新検討および更新手続きをお願いします。`;
  return body;
}

//本日アラート日のものを返す
function getAlertTargets(){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getSheetByName("契約管理");
  const vals = sh.getRange("A2:H").getValues().filter(x => x[0]);
  const obj = vals.map(x => new Contract(x));

  return obj.filter(x => dayjs.dayjs(x.AlertDate).isSame(dayjs.dayjs(),"day"));
}

//SS情報をオブジェクト化するためのクラス
class Contract{
  constructor(row){
    this.Title      = row[0]; //項目名
    this.Type       = row[1]; //カテゴリ
    this.AutoUpdate = row[2]; //自動更新
    this.EndDate    = row[3]; //契約満了日
    this.AlertDate  = row[4]; //通知日
    this.AlertTiming= row[5]; //通知時期
    this.AlertTo    = row[6]; //通知先
    this.Detail     = row[7]; //詳細
  }
}

 

3.ポイント

3-1.day.js

ライブラリ、day.jsを使っています。
使わなくても日付比較はできますが、使うと非常に楽ですので使用をオススメします。

使い方は、エディタ左側のライブラリのところで「+」を押し
下記のスクリプトIDを入力、追加してください。

1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB
day.js公式

3-2.トリガー設定

通知日=本日のデータがないかチェックしてメール送信する仕組みですので、
毎日起動する必要があります。

業務都合に合うお好きな時間帯を日次設定してください。

3-3.テンプレートリテラルについて

htmlBodyを作成する際、変数の値と文字列を組み合わせて作成しています。
文字列と変数を+で結合しても問題ありません。
ただ、当コードで使用しているテンプレートリテラルを活用すると
途中で柔軟に改行できることから可読性と保守性が増す場合が多いです。

使用方法としては、まず文字列を ` で囲います。
変数や定数の値を使用したい場合は`の中で${変数名}と書くと展開されます。

今回のような長めの文字列を扱う場合、有効ですので是非この機会に慣れておいてください。

4.さいごに

結構シンプルですぐ作れる内容でありながら、
業務にはバッチリ貢献してくれそうな例だと思います。

期日管理系ですと類似のものでこんな記事もありますので興味があれば参照してみてください。
GASでシートの入社日や退職日に合わせて自動でメールを送信する事例

ご活用いただけますと幸いです。ありがとうございました。

GASでシートの入社日や退職日に合わせて自動でメールを送信する事例

0.はじめに

従業員の入社や退職に際して自動的に連絡をしたい、
そんな時に活用できる例をご紹介します。

1.一覧スプレッドシートについて

今回使用するスプレッドシートはこんな形式だとします。
列構成を変えたい場合などもコードを少し変更すれば対応可能ですので柔軟に考えてください。
1行に1名の情報が入っていると楽です。

入社管理スプレッドシート

 

2.設定シートについて

メールの宛先や件名、内容などを簡単に管理できるように
スプレッドシート上に設定情報を持たせておきます。

メール設定シート

これをやると簡単に設定変更が可能になり汎用的なコードに近づきますが、
必要に応じてシートの編集権限を絞る、等の工夫が必要な場合もあるかと思います。

3.コード

ではこれらの内容を取得して、送信に繋げていきます。

//最初にライブラリdayjsを追加しておくこと
//スクリプトIDは1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB

/**
 * メイン処理
 * 入社日が本日のものだけを対象にするため、日次トリガーを設定する必要がある
 */
function sendEntryMail() {
  //シート情報を取得
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const targets = getEntryDatas(ss);
  if(targets.length == 0) return; //本日入社が無ければ終了
  
  //メール設定を取得
  const mailSetting = getMailSetting(ss, "入社");
  
  //1人ずつ送信
  for(let person of targets){
    //送信設定情報を作成
    let mailArgs = { cc: mailSetting.Cc,
                     bcc: mailSetting.Bcc,
                     from: mailSetting.FromAddress,
                     name: mailSetting.FromName,
                     htmlBody: mailSetting.Body.replace(/%対象者%/g,person.Name) //置換
                    };
    //送信
    GmailApp.sendEmail(person.Mail,mailSetting.Subject,"",mailArgs);
  }
}

/**
 * 本日入社のデータを取得する
 * @param {object} ss スプレッドシートオブジェクト
 * @return {object} todayEntry 本日入社のデータ 無ければ空配列 
 */
function getEntryDatas(ss){
  //スプレッドシートデータを取得
  const sh = ss.getSheetByName("入社");
  const datas = sh.getRange("A2:G").getValues().filter(x => x[0]);
  const obj = datas.map(x => new Entry(x));
  
  //本日入社分にフィルタ
  const todayEntry = obj.filter(x => dayjs.dayjs(x.Date).isSame(dayjs.dayjs(),"day"));
  return todayEntry;
}

//入社者のデータを整理するためのクラス
class Entry{
  /**
   * @param {array} row 入社シート1行分の配列
   */
  constructor(row){
    this.Date = row[0];
    this.Number = row[1];
    this.Mail = row[2];
    this.Name = row[3];
    this.Div = row[4];
    this.Prace = row[5];
    this.Notes = row[6];
  }
}

/**
 * 引数で指定したメール設定を返す
 * @param {object} ss スプレッドシートオブジェクト
 * @param {string} type 設定シートのどの項目を取得するか文字列で指定
 * @return {object} typeに応じた設定object
 */
function getMailSetting(ss,type){
  const sh = ss.getSheetByName("メール設定");
  const datas = sh.getRange("A2:H").getValues().filter(x => x[0]);
  const obj = datas.map(x => new MailSetting(x));

  const targetObj = obj.find(x => x.Type == type);
  return targetObj;
}

//メール設定を整理するためのクラス
class MailSetting{
  /**
   * @param {array} row メール設定シート1行分の配列
   */
  constructor(row){
    this.Type        = row[0];
    this.Cc          = row[1];
    this.Bcc         = row[2];
    this.FromAddress = row[3];
    this.FromName    = row[4];
    this.Subject     = row[5];
    this.Body        = row[6];
    this.Notes       = row[7];
  }
}

ポイントをいくつか挙げていきます。

3-1.day.jsについて

ライブラリ、day.jsを使っています。
使わなくても日付比較はできますが、使うと非常に楽ですので使用をオススメします。

使い方は、エディタ左側のライブラリのところで「+」を押し
下記のスクリプトIDを入力、追加してください。

1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB

day.js公式

3-2.トリガーについて

本日入社者がいないかチェックしてメール送信する仕組みですので、
毎日起動する必要があります。

社風によって適切な時間帯が異なると思いますので
お好きな時間帯を設定してください。

3-3.クラスとmapについて

スプレッドシート情報を取得した後、mapの中でnewしています。
各行の内容を用いてインスタンスオブジェクトを作成することで
効率的にキー付きの扱いやすいオブジェクトを作成できます。

非常に便利で、メイン処理を汚さないコードを書きやすくなります。
今回のコードはかなりシンプルで分かりやすいため、
クラスの理解がまだの方はじっくりと動きを確かめてみてください。

3-4.エラー処理について

この手の処理はあまりサーバー系のエラーが起きないので何もしていません。
が、こういう油断が足元をすくうのでtry catchで囲っておくべきです。

「サーバーエラーの場合try catchでも拾えないじゃない」と思う方は、
実行ログを作成して履行チェックを実施するGASを作成してください。
履行チェックGASは下記記事で使っていますので、よければ参照してみてください。
フォームのデータを取得できませんでした。のエラーに対処した話

4.さいごに

いかがでしたでしょうか。

私の会社ではシート上にもっと情報が多く、処理項目も多いため
メール送信部分だけを切り出してご紹介しました。

日次に応じてひな形に沿った対応を行う、という業務は
非常に自動化しやすい業務ですので、見つけたらチャンスです。

ぜひ活用してみてください。

GASでGoogleグループのエイリアス一覧を取得するコードと解説

0.はじめに

前回記事、
GASでGoogleグループのメンバー一覧を取得するコードと解説
のエイリアスバージョンです。

Admin画面からエイリアス設定を参照するためには、
少し操作ステップが必要になります。
スプレッドシートに一覧化しておけば参照が楽になり、
また編集履歴から古い情報も確認可能になります。

今回もドメイン名とシート名以外はコピペで使えます。
ぜひ参考にして、活用してみてください。

1.コード

function getGroupListAll(){  
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getSheetByName('test');

  let output = ([["グループ名", "アドレス", "エイリアス"]]);
  let domainList = ["ドメインを入力","管理ドメインが複数ある場合は続けて入力"];
  //ドメインに@は不要

  domainList.forEach(x=> pushArray(output,getGroupInfo(x)));
  inputSpace(output);
  sh.clear();
  sh.getRange(1,1,output.length,output[0].length).setValues(output);
}


//グループ情報を取得
function getGroupInfo(domain){
  let maxResults = 200;  
  let token = '';
  let result = []
  do{
    if(token==""){
      var groupsList = AdminDirectory.Groups.list({domain: domain, maxResults: maxResults})
    }else{
      var groupsList = AdminDirectory.Groups.list({domain: domain, maxResults: maxResults, pageToken: token})
    }
    if(groupsList.groups) {
      for(let g of groupsList.groups){
        let groupName = g.name//グループ名
        let groupMail = g.email//メールアドレス
        
        let nonEditable = g.nonEditableAliases; //自動付与設定されているエイリアス
        let editable = g.aliases; //自動付与以外に個別設定したエイリアス
        let concatedAliase = concatAliase(nonEditable, editable); //結合

        let forPush = [groupName,groupMail];
        result.push(forPush.concat(concatedAliase));
        }
      }
      token = groupsList.nextPageToken
    }while(token)
  return result
}


//エイリアス配列を必要に応じて結合して返す
function concatAliase(nonEditable, editable){
  if(nonEditable && editable){
    return nonEditable.concat(editable);
  }else if(nonEditable && !editable){
    return nonEditable;
  }else if(!nonEditable && editable){
    return editable;
  }else{
    return [];
  }
}


//破壊的に二次元配列に二次元配列を結合する
function pushArray(array1, array2){
    for(let arr2 of array2){
      array1.push(arr2);
  }
}


//長さの違う2次元配列に空白を入力する
function inputSpace(array){
  let wide = 0;
  for(let arr of array){
    if(arr.length>wide){
      wide = arr.length;
    }
  }
  for(let arr of array){
    while(arr.length<wide){
      arr.push('');
    }
  }
}

 

2.出力結果

こんな感じでスプレッドシートに出力されます。

エイリアスリスト取得結果

それぞれのエイリアス設定件数に応じて横に伸びていく形になります。

3.解説

3-1.AdminDirectory

グループ情報取得処理でAdminDirectoryを使用します。
エディタ左側の「サービス」から「AdminSDK」を追加しないと使えません。

Groups.listの公式リファレンス

3-2.構成

以下のような流れになっています。

ヘッダ、ドメインリストを作成

グループ情報を取得する中でエイリアス情報も一緒に取得
└2種類のエイリアスを結合

ヘッダにグループ情報を結合

行ごとの長さが異なるジャグ配列なので、これを貼り付け可能な二次元配列に整形

シートクリアして貼り付け

3-3.ポイント

3-3-1.取得上限

Groups.listは1リクエスト当たりの上限があります。
200件が上限のため200件以上の場合はトークンを取得して
次ページ分の情報取得へ進んでいく必要があります。

コード内で.nextPageToken()してトークンを取得、
do while(token)でトークンがある場合は次のページを取得しています。

3-3-2.nonEditableAliaseとaliaseについて

エイリアスには2種類あります。
アカウント作成と同時に固定的に生成するエイリアスを設定している場合、
そのエイリアスはnonEditableAliaseとして扱われます。

尚、nonEditableAliaseもaliaseも0件の場合はundefinedが返ってきますので、
concatAliase()でundefinedを除外して結合するようにしています。

4.活用方法

オススメの活用方法は「日次トリガーで参照用マスタにする」です。
Admin画面からエイリアス設定を参照するのは少し手間がかかりますし、
Admin画面を参照できる権限者にしかできない事です。

毎日深夜に更新されるよう設定しておけば工数ゼロなので、是非どうぞ。

5.さいごに

エイリアス参照が楽になる方法をお伝えしてきました。

今回の方法は、エイリアスを大量に設定していない限りは
グループメンバー一覧化と異なり情報量が少ないと思うので
タイムアウトやセル数上限についても気にしなくて良いかと思います。

ぜひ気軽に導入してみてくださいね。