FLEXSCHE

スタッフブログStaff Blog

フレクシェはメールを何日で返しているのか?Google Apps Scriptを使って検証してみた

2022/06/20
written by オカ

オカ

はじめまして、オカです。
こちらの記事にある通り、2022年1月に開発エンジニアとして入社しました。
入社以来、少しでも早く慣れるために夢の中でもFLEXSCHEについて考えているわけですが、ここではFLEXSCHEという製品そのものからは少し離れて、 FLEXSCHEの利用方法に関して質問していただくためのメーリングリスト(以下、ML)の返信にどれくらいの日数と時間がかかっているのか検証してみます。

検証には、Google Apps Script(以下、GAS)を使います。
GASはJavaScriptで記述できます。
FLEXSCHEではアドインの作成にTypeScriptも使えますが、 私自身はあまりTypeScriptおよびJavaScriptを書いたことがないので、練習がてら使ってみようというわけです。

ちなみに、MLは原則2営業日以内に返信するという方針で対応しています。
果たして、守られているのでしょうか??


対象と返信日数・時間について

対象は、記事執筆時点から直近3か月のメールとします。また、最初の質問メールとそれに対する返信メールのみとし、その後のメールのやり取りは対象外とします。
返信の日数と時間は営業日・営業時間(土日祝日を除く9:00-18:00)を考慮します。
金曜日の17:00に受信したメールを月曜日の10:00に返した場合、返信日数は1日、返信時間は2時間という具合です。
営業時間外に受信したメールは、翌営業日9:00からの起算とします。
例えば土曜10:00に受信したメールを月曜10:00に返した場合、月曜9:00から起算開始なので返信日数は0日、返信時間は1時間です。


いざ、プログラミング

それでは、コードを書いていきます。
GASのプロジェクトの作成方法等は省略します。
コードなんていいから結果だけ見たい!という方はこちら


Gmailからメール情報を取得する

Gmailの情報は GmailAppで取得することができます。
以下のようにすると、条件に合ったメールがスレッド単位で取得できます。
MLの件名と直近3か月を条件として、以下のように検索します。

const prefix = ""; // MLの件名のプレフィックスを設定する
const query = "subject:"+ prefix + " newer_than:3m";
const threads = GmailApp.search(query);

さらに、取得したスレッドからメッセージを取得します。

const messagesForThreads = GmailApp.getMessagesForThreads(threads);

このスレッド毎のメッセージに対して、処理を行っていきます。

Googleカレンダーから祝日情報を取得する

Googleカレンダーには、「日本の祝日」というカレンダーがあります。
このカレンダーの情報を取得して、祝日の判定をします。
カレンダー情報は CalendarApp を使って取得できます。


// 休日判定
function IsHoliday(date)
{
  // 土日判定  
  const day = date.getDay();
  if(day === 0 || day === 6) return true;

  // 祝日判定
  const id = 'ja.japanese#holiday@group.v.calendar.google.com' // Googleカレンダー「日本の祝日」のID
  const cal = CalendarApp.getCalendarById(id);
  const events = cal.getEventsForDay(date);
  // イベントがあれば祝日
  if(events.length) return true;

  return false;
}


取得したメールとカレンダーの情報から返信日数を計算する。

営業日を考慮した日数差は、先ほどの休日判定処理を使って以下のように取得します。

// 営業日数差を取得
// 金曜(平日)から翌週火曜(月曜が祝日)の差は1日
function GetBusinessDiffDays(receivedDate, replyDate)
{
  // 時刻を切り捨てる
  var date1 = new Date(receivedDate.getFullYear(), receivedDate.getMonth(), receivedDate.getDate());
  var date2 = new Date(replyDate.getFullYear(), replyDate.getMonth(), replyDate.getDate());

  var diff = 0;
  while(date1.getTime() < date2.getTime())
  {
    date2.setDate(date2.getDate() - 1);
    if(!IsHoliday(date2)) diff += 1
  }
  return diff;
}

この処理に日時を渡すために、取得したスレッド毎のメッセージ群から最初のメールとそれに対する返信メールを取得する必要があります。長くなるので、その他の処理も含むコード全文は最後に載せます。
最終的には、実行すると以下のようにスプレッドシートに結果を出力するコードを作成しました。


gas_sheet.png


結果発表

各メールの返信日数・時間を計算し、スプレッドシートに書き出すところまで行いました。
それらを集計して、返信日数ごとの件数、返信時間ごとの件数、返信時間の平均・最大・最小時間を出したグラフと表がこちらです。

gas_result1.png

gas_result2.png

gas_result3.png


直近3か月での件数が159件で、その返信日数の内訳は、

0日:132件  
1日:24件  
2日:3件  
3日以上:0件

でした!
2営業日以内というのはしっかり守られているようです!
当日中の返信がほとんどで、しかも2時間以内の返信が多いです。
平均3.4時間というのはかなり優秀ではないでしょうか。


いかがでしたか。

返信時間も優秀ですが、対応している件数も3か月で159件というのはかなり多いのではないでしょうか。
私がフレクシェに入社して三大驚いたことの一つが、この件数の多さです。
それほど、皆様にお使い頂けている証拠だと思います。
今後も皆様がFLEXSCHEを安心してお使いいただけるよう頑張っていきますのでよろしくお願いいたします!




コード全文

以下、コード全文です。
間違いや、より良い書き方などがあれば教えていただけると幸いです!

function main() {
  const prefix = ""; // メール件名のプレフィックスを設定する
  const guestAddress = "";  // 質問者様匿名アドレスを設定
  const query = "subject:"+ prefix + " newer_than:3m";

  const threads = GmailApp.search(query);
  const messagesForThreads = GmailApp.getMessagesForThreads(threads);

  const values = [];
  for(const messages of messagesForThreads){
    // スレッドの最初のメールがフレクシェ担当者の場合は対象外
    if(messages[0].getFrom() != guestAddress){
      continue;
    }
    const firstMessage = messages[0];
    var reMessage = messages[0];
    var existReMsg = false;
    for(var i = 1; i < messages.length; i++){
      // 質問者から連投されたメールはスキップする
      if(messages[i].getFrom() == guestAddress || !messages[i].getSubject().startsWith('[' + prefix)){
        continue;
      }
      else{
        reMessage = messages[i];
        existReMsg = true;
        break;
      }
    }

    // 返信メールが存在しないスレッドは処理しない
    if(!existReMsg) continue;

    // 返信までの日数・かかった時間
    const receivedDate = ConvertTime(firstMessage.getDate());
    const replyDate = reMessage.getDate();
    const reDays = GetBusinessDiffDays(receivedDate, replyDate);
    const reDuration = GetReplyDuration(receivedDate, replyDate);

    // スプレッドシートに書き出す用
    const record = [
      firstMessage.getDate(),
      receivedDate,
      firstMessage.getFrom(),
      firstMessage.getSubject(),
      replyDate,
      reMessage.getFrom(),
      reDays,
      reDuration,
    ];
    values.push(record);
  }

  // スプレッドシートに書き出す
  // ヘッダー
  const header = [
    ["受信日時",
    "営業日時に変換した受信日時",
    "送信者",
    "件名",
    "返信日時",
    "返信者",
    "返信日数",
    "返信時間(ミリ秒)"]
  ];
  SpreadsheetApp.getActiveSheet().getRange(1, 1, 1, header[0].length).setValues(header);

  // 結果
  if(values.length > 0){
    SpreadsheetApp.getActiveSheet().getRange(2, 1, values.length, values[0].length).setValues(values);
  }
}

// 返信にかかった時間(ミリ秒)
function GetReplyDuration(receivedDate, replyDate)
{
  // 同日の場合は単純に引く
  if(SameDay(receivedDate, replyDate))
  {
    return replyDate - receivedDate;
  }
  else
  {
    const until18 = d => new Date(d.getFullYear(), d.getMonth(), d.getDate(), 18 , 0, 0, 0) - d;
    const from9 = d => d - new Date(d.getFullYear(), d.getMonth(), d.getDate(), 9 , 0, 0, 0);
    const diffDays = GetBusinessDiffDays(receivedDate, replyDate);

    // receivedDateの18時までの時間 + replyDateの9時からの時間 + (営業日数差 - 1)*9 ※9:00-18:00で9時間
    // ミリ秒に変換する
    return until18(receivedDate) + from9(replyDate) + ((diffDays - 1) * 9 * 60 * 60 * 1000);
  }
}

// 引数の日時が営業時間外の場合、翌営業日の9:00に変換する
function ConvertTime(date)
{
  // 営業時間内の場合はそのまま返す
  if(IsBusinessHour(date)) return date;

  // 営業時間外の場合、
  // 当日9:00より前の時間は当日9:00に、18時以降の場合は翌日9:00に変更し、
  // 営業日になるまで+1日を繰り返す。
  var newDate = new Date(date);
  if(newDate.getHours() >= 18) newDate.setDate(date.getDate() + 1);
  newDate.setHours(09);
  newDate.setMinutes(00);
  newDate.setSeconds(00);

  while(!IsBusinessHour(newDate))
  {
    newDate.setDate(newDate.getDate() + 1);
  }

  return newDate;
}

// 休日判定
function IsHoliday(date)
{
  // 土日判定  
  const day = date.getDay();
  if (day === 0 || day === 6) return true;

  // 祝日判定
  const id = 'ja.japanese#holiday@group.v.calendar.google.com' // Googleカレンダー「日本の祝日」のID
  const cal = CalendarApp.getCalendarById(id);
  const events = cal.getEventsForDay(date);
  // イベントがあれば祝日
  if (events.length) return true;

  return false;
}

// 営業時間判定
function IsBusinessHour(date)
{
  // 9~18時の間でなければ時間外
  const hour = date.getHours();
  if(hour < 9 || hour >= 18) return false;

  // 時間内であれば、休日かどうか
  return !IsHoliday(date);
}

// 営業日数差を取得
// 金曜(平日)から翌週火曜(月曜が祝日)の差は1日
function GetBusinessDiffDays(receivedDate, replyDate)
{
  // 時刻を切り捨てる
  var date1 = new Date(receivedDate.getFullYear(), receivedDate.getMonth(), receivedDate.getDate());
  var date2 = new Date(replyDate.getFullYear(), replyDate.getMonth(), replyDate.getDate());

  var diff = 0;
  while(date1.getTime() < date2.getTime())
  {
    date2.setDate(date2.getDate() - 1);
    if(!IsHoliday(date2)) diff += 1
  }
  return diff;
}

// 同じ日かどうか
function SameDay(day1, day2)
{
  if(day1.getFullYear() != day2.getFullYear()) return false;
  if(day1.getMonth() != day2.getMonth()) return false;
  if(day1.getDate() != day2.getDate()) return false;

  return true;
}

ユーザー

PAGETOP