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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

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

Q&A

解決済

2回答

2810閲覧

【GAS】チェックがついた宛先を取得し、メール送信

shirogohan

総合スコア14

Google スプレッドシート

Google スプレッドシートは、フリーで利用できる表計算ソフト。Webアプリのためインターネットに接続することで利用できます。チャートやグラフの作成のほか、シートを他のユーザーと共有したり、同時に作業を進めることも可能です。

Google Apps Script

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

0グッド

1クリップ

投稿2022/10/26 08:27

前提

GAS超初心者です。
GASを使用し、メール送信ツールを作成していますが、行き詰っています。
どなたかアドバイスをいただけますと大変幸いです。
よろしくお願いいたします。

実現したいこと

チェックボックスにチェックがついた宛先のデータのみ取得し、
メールの下書き作成と送信を行いたい。

発生している問題・エラーメッセージ

エラーメッセージは出ていませんが、チェックのついたデータのみを取得できません。

追記

  • 参考サイトと異なる部分は宛先シートのセルAにチェックボックスを設けています。
  • チェックボックスを押した時点でのメール送信ではなく、チェックが入っているデータを取得→差し込み→送信もしくは下書き作成(ボタン形式)です。

該当のソースコード

/*Step1:グローバル定数 -------------------------------------------------------------*/ const ss = SpreadsheetApp.getActiveSpreadsheet(); const mainSheet = ss.getSheetByName("Main"); /*Step2:シートオープン時にプルダウンの選択肢をセットする -------------------------------------------------------------*/ function onOpen() { setRecipientListPulldown(); } /*Step3:送信/下書きボタン押下時の処理|onClickCreateDrafts -------------------------------------------------------------*/ function onClickSendEmails() { const answer = Browser.msgBox("メールを一括送信します。よろしいですか。", Browser.Buttons.OK_CANCEL); if (answer === "ok") { batchProcessEmails("sendEmail"); Browser.msgBox("処理が完了しました。「処理結果」列を確認してください。"); } } function onClickCreateDrafts() { const answer = Browser.msgBox("メールの下書きを一括作成します。よろしいですか。", Browser.Buttons.OK_CANCEL); if (answer === "ok") { batchProcessEmails("createDraft"); Browser.msgBox("処理が完了しました。「処理結果」列を確認してください。"); } } /*Step4:宛先シート全行分のメールを処理する|batchProcessEmails -------------------------------------------------------------*/ function batchProcessEmails(action) { // Mainシートの入力値を取得 const inputValues = getInputValues(); // ログ列(R列)をクリア ss.getSheetByName(inputValues.recipientListName).getRange("R2:R").clear(); // 宛先シートの入力内容を取得 const targets = getTargets(inputValues.recipientListName); // 宛先シートの全行に対して送信(または下書き)を実行 const logs = []; for (const target of targets) { const result = processEmail(inputValues, target, action); logs.push([result]); } // ログ列(Q列)に結果を貼り付け ss.getSheetByName(inputValues.recipientListName) .getRange(`R2:R${logs.length + 1}`) .setValues(logs); } /*Step5:メールを1件送信または下書き作成する|processEmail -------------------------------------------------------------*/ function processEmail(inputValues, target, action) { try { // タイトル・本文に差込を適用 const replacedMailTitle = getReplacedString(inputValues.mailTitle, target.replaceList); const replacedMailBody = getReplacedString(inputValues.mailBody, target.replaceList); // 添付ファイルを取得 const attachements = [ ...getAttachements(inputValues.attachmentFolderId, inputValues.commonAttachmentNames), ...getAttachements(inputValues.attachmentFolderId, target.eachAttachmentNames) ] /* メールオプションの設定 -------------------------------------------------------------*/ const options = {}; if (inputValues.senderEmail) options.from = inputValues.senderEmail; if (inputValues.senderName) options.name = inputValues.senderName; if (target.cc) options.cc = target.cc; if (target.bcc) options.bcc = target.bcc; if (attachements.length) options.attachments = attachements; /* メール送信 or 下書き作成 -------------------------------------------------------------*/ if (action === "sendEmail") { GmailApp.sendEmail(target.to, replacedMailTitle, replacedMailBody, options); } else { GmailApp.createDraft(target.to, replacedMailTitle, replacedMailBody, options); } return "Success"; } catch(error) { return error; } } /*Step6:文字列, 差込リストから差込後のテキストを取得する|getReplacedString -------------------------------------------------------------*/ function getReplacedString(string, replaceList) { let replacedString = string; for (const item of replaceList) { const before = new RegExp(`\{\{ *${item.before} *\}\}`, "g"); replacedString = replacedString.replace(before, item.after); } return replacedString; } //★:チェックボックスの判定 function getCheckbox() { const myCell = mainSheet.getActiveCell(); const rule = myCell.getDataValidation(); if (rule != null) { const criteria = rule.getCriteriaType(); const status = myCell.getValue() if ( criteria == 'CHECKBOX' && status == true) { const row = myCell.getRow(); getInputValues(); } } } /*Step7:mainシートに入力された値を取得する|getInputValues -------------------------------------------------------------*/ function getInputValues() { const inputValues = { attachmentFolderId: mainSheet.getRange("B2").getValue(), // 添付ファイル格納フォルダID mailTitle: mainSheet.getRange("B5").getValue(), // メールタイトル mailBody: mainSheet.getRange("B6").getValue(), // 本文 commonAttachmentNames: [ mainSheet.getRange("B7").getValue(), // 共通添付ファイル名1 mainSheet.getRange("B8").getValue(), // 共通添付ファイル名2 mainSheet.getRange("B9").getValue(), // 共通添付ファイル名3 ], recipientListName: mainSheet.getRange("B12").getValue(), // 宛先リストシート名 } return inputValues; } /*Step8:宛先リストシート名から、シートの内容を配列で取得する -------------------------------------------------------------*/ function getTargets(recipientListName) { const targetsSheet = ss.getSheetByName(recipientListName); const sheetData = targetsSheet.getDataRange().getValues(); const header = sheetData[0]; const contents = sheetData.slice(1); const targets = contents.map((row) => { return { checkbox: [0], // チェックボックス to: row[1], // 宛先メールアドレス cc: row[2], // ccアドレス bcc: row[3], // bccアドレス replaceList: getReplaceList(header, row), // 置換内容 eachAttachmentNames: [ row[14], // 個別添付ファイル名1 row[15], // 個別添付ファイル名2 row[16], // 個別添付ファイル名3 ] }; }); return targets; } /*Step8:ヘッダー行・値行から、置換前・置換後を格納したオブジェクトの配列を返す -------------------------------------------------------------*/ function getReplaceList(header, row) { const beforeStrings = header.slice(4, 14); // 4 - 12列目が差込項目 const afterStrings = row.slice(4, 14); // 4 - 12列目が差込項目 const replaceList = []; beforeStrings.forEach((beforeString, index) => { if (beforeString) replaceList.push({ before: beforeString, after: afterStrings[index] }); }); return replaceList; } 以下省略

参考サイト

メール送信:
https://web-breeze.net/gmail-insertion-sending/
チェックボックス:
https://masagoroku.com/

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

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

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

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

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

guest

回答2

0

※文字数(1つの回答欄に10000字以上は投稿不可)の関係で回答を2つに分けます。

まず修正後の全体のコードを下記に記載します。(修正した部分については後述します) 

js

1/*Step1:グローバル定数 2-------------------------------------------------------------*/ 3const ss = SpreadsheetApp.getActiveSpreadsheet(); 4const mainSheet = ss.getSheetByName("Main"); 5 6/*Step2:シートオープン時にプルダウンの選択肢をセットする 7-------------------------------------------------------------*/ 8function onOpen() { 9 setRecipientListPulldown(); 10} 11 12/*Step3:送信/下書きボタン押下時の処理|onClickCreateDrafts 13-------------------------------------------------------------*/ 14function onClickSendEmails() { 15 const answer = Browser.msgBox("メールを一括送信します。よろしいですか。", Browser.Buttons.OK_CANCEL); 16 if (answer === "ok") { 17 batchProcessEmails("sendEmail"); 18 Browser.msgBox("処理が完了しました。「処理結果」列を確認してください。"); 19 } 20} 21 22function onClickCreateDrafts() { 23 const answer = Browser.msgBox("メールの下書きを一括作成します。よろしいですか。", Browser.Buttons.OK_CANCEL); 24 if (answer === "ok") { 25 batchProcessEmails("createDraft"); 26 Browser.msgBox("処理が完了しました。「処理結果」列を確認してください。"); 27 } 28} 29 30/*Step4:宛先シート全行分のメールを処理する|batchProcessEmails 31-------------------------------------------------------------*/ 32function batchProcessEmails(action) { 33 // Mainシートの入力値を取得 34 const inputValues = getInputValues(); 35 // ログ列(R列)をクリア 36 ss.getSheetByName(inputValues.recipientListName).getRange("R2:R").clear(); 37 // 宛先シートの入力内容を取得 38 const targets = getTargets(inputValues.recipientListName); 39 // 宛先シートの全行に対して送信(または下書き)を実行 40 const logs = []; 41 for (const target of targets) { 42 const result = processEmail(inputValues, target, action); 43 logs.push([result]); 44 } 45 // ログ列(Q列)に結果を貼り付け 46 ss.getSheetByName(inputValues.recipientListName) 47 .getRange(`R2:R${logs.length + 1}`) 48 .setValues(logs); 49} 50 51/*Step5:メールを1件送信または下書き作成する|processEmail 52-------------------------------------------------------------*/ 53function processEmail(inputValues, target, action) { 54 try { 55 // タイトル・本文に差込を適用 56 const replacedMailTitle = getReplacedString(inputValues.mailTitle, target.replaceList); 57 const replacedMailBody = getReplacedString(inputValues.mailBody, target.replaceList); 58 59 // 添付ファイルを取得 60 const attachements = [ 61 ...getAttachements(inputValues.attachmentFolderId, inputValues.commonAttachmentNames), 62 ...getAttachements(inputValues.attachmentFolderId, target.eachAttachmentNames) 63 ] 64 65 /* メールオプションの設定 66 -------------------------------------------------------------*/ 67 const options = {}; 68 if (inputValues.senderEmail) options.from = inputValues.senderEmail; 69 if (inputValues.senderName) options.name = inputValues.senderName; 70 if (target.cc) options.cc = target.cc; 71 if (target.bcc) options.bcc = target.bcc; 72 if (attachements.length) options.attachments = attachements; 73 74 /* メール送信 or 下書き作成 75 -------------------------------------------------------------*/ 76 if (action === "sendEmail") { 77 GmailApp.sendEmail(target.to, replacedMailTitle, replacedMailBody, options); 78 } else { 79 GmailApp.createDraft(target.to, replacedMailTitle, replacedMailBody, options); 80 } 81 return "Success"; 82 } catch(error) { 83 return error; 84 } 85} 86 87/*Step6:文字列, 差込リストから差込後のテキストを取得する|getReplacedString 88-------------------------------------------------------------*/ 89function getReplacedString(string, replaceList) { 90 let replacedString = string; 91 for (const item of replaceList) { 92 const before = new RegExp(`\{\{ *${item.before} *\}\}`, "g"); 93 replacedString = replacedString.replace(before, item.after); 94 } 95 return replacedString; 96} 97 98//★:チェックボックスの判定 99 100function getCheckbox() { 101 const myCell = mainSheet.getActiveCell(); 102 const rule = myCell.getDataValidation(); 103 if (rule != null) { 104 const criteria = rule.getCriteriaType(); 105 const status = myCell.getValue() 106 if ( criteria == 'CHECKBOX' && status == true) { 107 const row = myCell.getRow(); 108 getInputValues(); 109 } 110 } 111} 112 113/*Step7:mainシートに入力された値を取得する|getInputValues 114-------------------------------------------------------------*/ 115function getInputValues() { 116 const inputValues = { 117 attachmentFolderId: mainSheet.getRange("B2").getValue(), // 添付ファイル格納フォルダID 118 mailTitle: mainSheet.getRange("B5").getValue(), // メールタイトル 119 mailBody: mainSheet.getRange("B6").getValue(), // 本文 120 commonAttachmentNames: [ 121 mainSheet.getRange("B7").getValue(), // 共通添付ファイル名1 122 mainSheet.getRange("B8").getValue(), // 共通添付ファイル名2 123 mainSheet.getRange("B9").getValue(), // 共通添付ファイル名3 124 ], 125 recipientListName: mainSheet.getRange("B12").getValue(), // 宛先リストシート名 126 } 127 return inputValues; 128} 129 130/*Step8:宛先リストシート名から、シートの内容を配列で取得する 131-------------------------------------------------------------*/ 132function getTargets(recipientListName) { 133 const targetsSheet = ss.getSheetByName(recipientListName); 134 const sheetData = targetsSheet.getDataRange().getValues(); 135 const header = sheetData[0]; 136 const contents = sheetData.slice(1); 137 const targets = contents.map((row) => { 138 return { 139 checkbox: row[0], // [修正] // チェックボックス 140 to: row[1], // 宛先メールアドレス 141 cc: row[2], // ccアドレス 142 bcc: row[3], // bccアドレス 143 replaceList: getReplaceList(header, row), // 置換内容 144 eachAttachmentNames: [ 145 row[14], // 個別添付ファイル名1 146 row[15], // 個別添付ファイル名2 147 row[16], // 個別添付ファイル名3 148 ] 149 }; 150 }).filter(e => e.checkbox === true); // [追加] 151 console.log(targets); 152 return targets; 153} 154 155 156/*Step8:ヘッダー行・値行から、置換前・置換後を格納したオブジェクトの配列を返す 157-------------------------------------------------------------*/ 158function getReplaceList(header, row) { 159 const beforeStrings = header.slice(4, 14); // 5 - 14列目が差込項目 160 const afterStrings = row.slice(4, 14); // 5 - 14列目が差込項目 161 const replaceList = []; 162 beforeStrings.forEach((beforeString, index) => { 163 if (beforeString) replaceList.push({ 164 before: beforeString, 165 after: afterStrings[index] 166 }); 167 }); 168 return replaceList; 169} 170 171/* ------------------------------------------- 172 getAttachments 173 フォルダID, ファイル名配列からファイル配列を取得する 174 -------------------------------------------- */ 175 function getAttachements(folderId, fileNames) { 176 const attachements = []; 177 fileNames.forEach((fileName) => { 178 if (fileName) attachements.push(getAttachement(folderId, fileName)); 179 }) 180 return attachements; 181 } 182 183/* ---------------------------------------- 184getAttachment 185フォルダID, ファイル名からファイルを取得する 186------------------------------------------- */ 187function getAttachement(folderId, fileName) { 188 const files = DriveApp.getFolderById(folderId).getFilesByName(fileName); 189 if (!files.hasNext()) throw Error(`添付ファイル ${fileName} が存在しません。`); 190 file = files.next(); 191 if (files.hasNext()) throw Error(`添付ファイル ${fileName} が複数存在します。`); 192 return file; 193} 194 195/* ----------------------------------------- 196 setRecipientListPulldown 197 宛先リストシート名のプルダウンに選択肢をセットする 198 ------------------------------------------ */ 199function setRecipientListPulldown() { 200 // Mainシート以外のシート名を取得 201 const values = ss.getSheets().map((sheet) => { 202 const sheetName = sheet.getName() 203 return sheetName !== "Main" 204 ? sheetName 205 : "" 206 }) 207 // 入力規則を作成 208 const rule = SpreadsheetApp.newDataValidation().requireValueInList(values).build(); 209 // MainシートのB14セルにルールを適用 210 ss.getSheetByName("Main").getRange("B14").setDataValidation(rule); 211}

(修正部分の説明については別の回答欄に記載しています)

投稿2022/10/26 14:47

編集2022/10/26 22:31
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

ベストアンサー

(続き)
修正後のコード全体については、別(上)の回答欄に記載しています。

修正したところとポイント

① チェックボックスの状態を格納する部分

「/*Step8:宛先リストシート名から、シートの内容を配列で取得する」(130行目あたりから)を見ると

js

1function getTargets(recipientListName) { 23... 4...... 5 const targets = contents.map((row) => { 6 return { 7 checkbox: [0], // チェックボックス

となっていました。
この一番下の行の
「checkbox: [0]」
というところを
「checkbox: row[0]」
に修正しています。

おそらく単純ミスではないかな、と思いました。
その下の行では

js

1 to: row[1], // 宛先メールアドレス 2 cc: row[2], // ccアドレス 3 bcc: row[3],

のように、「row[x]」という形になっていますね。

row[0]ではなく[0]としてしまうと、単に数字のゼロが格納されるだけなので、チェックボックスの状態を格納したことになりません。

② チェックボックスを入れた宛先だけを送付させるには?

ご質問の中にある「チェックのついたデータのみを取得できません。」という文から察するに、
質問者さんのコードだと「チェックを入れた行だけメール送信したいのに、全部の行に記入したメールが送信されてしまう。」ということであると推測しました。

そして上に書いた ① の部分を直して試しに「送信」ボタンまたは「下書き」ボタンを押しても、やはり全部送信されてしまいます。

これはなぜかというと「チェックを入れた行だけを送信対象とする」という、条件によるふるい分け処理が欠けているからです。

ではどのようにするかについて説明します。
まず、送信対象を作っている部分は(①で言及したのと同じ) Step8 : getTargets() 関数の中の、以下の部分になります。(もとのコードのままです)

js

1 const targets = contents.map((row) => { 2 return { 3 checkbox: row[0], // [修正] // チェックボックス 4 to: row[1], // 宛先メールアドレス 5 cc: row[2], // ccアドレス 6 bcc: row[3], // bccアドレス 7 replaceList: getReplaceList(header, row), // 置換内容 8 eachAttachmentNames: [ 9 row[14], // 個別添付ファイル名1 10 row[15], // 個別添付ファイル名2 11 row[16], // 個別添付ファイル名3 12 ] 13 }; 14 });

contents.map の「map」 は配列を作る関数です。
「contents」 には、宛先シートの全行データが格納されています。
そこから1行1行取り出して、必要な列だけ格納している処理が、上記のコードの部分です。

では、チェックを付けた行だけに絞るにはどうすればよいでしょうか?

ここで「filter」という関数を使用します。
filter 関数は、配列のうち「条件にあてはまる要素だけ」を抜き出して、新たな配列として返す関数です。
簡単な例で説明します。
たとえば
numbers = [ 1, 2, 3, 4, 5, 6]
という配列があるとして、この配列から、偶数だけを抜き出したいとします。
これをコードで表すと

js

1const numbers = [1, 2, 3, 4, 5, 6]; 2const new_numbers = numbers.filter(e => e % 2 === 0); 3console.log(new_numbers); 4 5>>> [2, 4, 6] 6と表示される

となります。
filterの中の「e => e % 2 === 0」というのが条件で「numbersの要素の中で、2で割った余りが0になる数だけを抜き出す」という条件を指定していることになります。

これを応用して、上記の宛先リストのコードに適用すると
「チェックボックスにチェックが入っている(=チェックボックスの値が true)であるものだけを抜き出す」という条件になります。

これをコードで書くと

js

1 const targets = contents.map((row) => { 2 return { 3 checkbox: row[0], // [修正] // チェックボックス 4 to: row[1], // 宛先メールアドレス 5 cc: row[2], // ccアドレス 6 bcc: row[3], // bccアドレス 7 replaceList: getReplaceList(header, row), // 置換内容 8 eachAttachmentNames: [ 9 row[14], // 個別添付ファイル名1 10 row[15], // 個別添付ファイル名2 11 row[16], // 個別添付ファイル名3 12 ] 13 }; 14 }).filter(row => row.checkbox === true); // [追加]

というようになります。(filter関数の条件を最後に追加)

いったん、contents =全行について列を整えた配列を作り、さらにその配列の中から「チェックボックスにチェックが入っている行」だけを抜き出した新たな配列を作る、という処理になります。

投稿2022/10/26 14:47

編集2022/10/26 22:40
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

shirogohan

2022/10/27 05:34

まさにやりたかったことを実装することができました!!丁寧に分かりやすく教えてくださり、本当に感謝しています。すごく勉強になりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問