🧑‍🎄

社内アンケートを社員にリマインドするbotをつくってみた

2024/12/13に公開

この記事は NE Advent Calendar 2024 の13日目の記事です。

こんにちわ。
ロカルコ事業部でマネージャーをしている@yaginuuuです。

前置き

NE社ではなにかイベントがあったりしたあとに「振り返り&FB文化」があります。
また、社内MVPを決めるアンケートがあったりします。
NE社では上記をやる場合、googleアンケートで回答を募り、それを運営メンバーが集計して..ということをその都度しています。
みなさんの会社はどうでしょうか?

このとき、1番に困るのが、アンケート結果の「母数」が足りないことだったりします。
運営メンバーやマネージャー陣がリマインドしてみても、なにかこう"強制的"で、よくないよくないよくない。
こんなことは早くエンジニアリングで解決した方がいいよねという気持ちになったので、この機会にサクッと作ってみることにしました。

作ったもの

作ると言っても、chatGPTに要件ステップを伝えて、出来上がったものをちょちょっと修正しただけです。(検証も合わせて、1h程度)

下記のgasスクリプトを作成したいです。
・スクリプトの目的
  ・「社内アンケートを各メンバーにslackのDMでリマインド通知したい」
・スクリプトの機能
 ・「アンケートリスト」シートには下記の列に情報を記載
  ・アンケート名
  ・アンケート結果URL
  ・アンケートURL
  ・期限
 ・「slackアカウントリスト」シートには下記の列に情報を記載
  ・メールアドレス
  ・メンバーID
 ・1. 「アンケートリスト」シートから「期限」列の期限までリマインド通知する
 ・2. 「slackアカウントリスト」からアカウント情報を抽出する
 ・3. 「アンケートリスト」シートからアンケート結果URLを抽出し、該当のスプレッドシート内のシート情報を取得(アンケートの回答)
 ・4. 3で取得した情報の「メールアドレス」列に存在しないが、2の「slackアカウントリスト」シートの「メールアドレス」列に存在するslackアカウントを抽出する
 ・5. 4で抽出したslackアカウントに対して、DMメッセージでリマインドを行う
// Slack Botトークンを設定
const SLACK_BOT_TOKEN = "xoxb-";
const SLACK_API_URL = "https://slack.com/api/chat.postMessage";

// シートの名前を設定
const SURVEY_SHEET_NAME = "アンケートリスト";
const SLACK_SHEET_NAME = "slackアカウントリスト";

// メイン関数
function sendSurveyReminders() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();

  // シートデータを取得
  const surveySheet = ss.getSheetByName(SURVEY_SHEET_NAME);
  const surveyData = surveySheet.getDataRange().getValues();
  const slackSheet = ss.getSheetByName(SLACK_SHEET_NAME);
  const slackData = slackSheet.getDataRange().getValues();
  const emailToSlackID = createEmailToSlackIDMap(slackData);
  const now = new Date();

  for (let i = 1; i < surveyData.length; i++) { // ヘッダーを除く
    const [surveyName, surveyResultURL, surveyURL,deadline] = surveyData[i];
    const deadlineDate = new Date(deadline);
    const responseSheet = SpreadsheetApp.openByUrl(surveyResultURL)

  const responseData = responseSheet.getDataRange().getValues();
    const responseEmails = getEmailsFromResponse(responseData);

    // 期限が未来の場合のみリマインド
    if (now <= deadlineDate) {
      const unmatchedSlackIDs = findUnmatchedSlackIDs(emailToSlackID, responseEmails);
      
      // Slackでリマインドメッセージを送信
      unmatchedSlackIDs.forEach(slackID => {
        sendSlackReminder(slackID, surveyName, surveyURL, deadlineDate);
      });
    }
  }
}

// メールアドレス → SlackメンバーIDのマッピングを作成
function createEmailToSlackIDMap(slackData) {
  const map = {};
  for (let i = 1; i < slackData.length; i++) { // ヘッダーを除く
    const [email, slackID] = slackData[i];
    map[email] = slackID;
  }
  return map;
}

// アンケート回答データからメールアドレスを取得
function getEmailsFromResponse(responseData) {
  const emails = [];
  const emailColumnIndex = responseData[0].indexOf("メールアドレス");
  if (emailColumnIndex === -1) {
    throw new Error("アンケート回答シートに「メールアドレス」列が見つかりません。");
  }

  for (let i = 1; i < responseData.length; i++) { // ヘッダーを除く
    emails.push(responseData[i][emailColumnIndex]);
  }

  return emails;
}

// Slackアカウントで未回答のアカウントを抽出
function findUnmatchedSlackIDs(emailToSlackID, responseEmails) {
  return Object.keys(emailToSlackID).filter(email => !responseEmails.includes(email)).map(email => emailToSlackID[email]);
}

// Slackにリマインド通知を送る
function sendSlackReminder(slackID, surveyName, surveyURL, deadlineDate) {
  const payload = {
    channel: slackID,
    text: `📝 *アンケートリマインド*\n` +
          `アンケート名: *${surveyName}*\n` +
          `アンケートURL: ${surveyURL}\n` +
          `回答期限: ${deadlineDate.toLocaleDateString()}\n` +
          `ぜひ回答をお願いします!`,
  };

  const options = {
    method: "post",
    contentType: "application/json",
    headers: {
      Authorization: `Bearer ${SLACK_BOT_TOKEN}`,
    },
    payload: JSON.stringify(payload),
  };

  const response = UrlFetchApp.fetch(SLACK_API_URL, options);
  const result = JSON.parse(response.getContentText());

  if (!result.ok) {
    Logger.log(`Slackメッセージ送信失敗 (Slack ID: ${slackID}): ${result.error}`);
  } else {
    Logger.log(`Slackメッセージ送信成功 (Slack ID: ${slackID})`);
  }
}

// トリガーを作成する(例: 毎日実行)
function createTrigger() {
  ScriptApp.newTrigger("sendSurveyReminders")
    .timeBased()
    .everyDays(1)
    .atHour(9)
    .create();
}

実行結果


あとがき

これを利用すれば、アンケート母数も増えて、みんなハッピーですね!

私は普段はもうコードを書く機会が少なくなってしまっていますが、
エンジニアとして「なぜつくるか?」「なにをつくるか?」って大事ですよね!
プロダクトへのコミットも超大事ですが、社内バリューへのコミットも、こんな感じでサクッとできちゃうので、おすすめです。

NE株式会社の開発ブログ

Discussion