SANANEBLOG
業務効率化 PR

【Workplace】Workchatをcsvエクスポートする方法!APIを使わず可能に

記事内に商品プロモーションを含む場合があります

はじめに

今回はWorkplace for MetaのWorkchatのチャット情報をcsvファイルとしてエクスポートする方法をご紹介します。

SANANE

APIを使用せずに実現可能な方法なので、技術的な知識がなくても簡単に実行できます。

通常Workchatのデータをダウンロードするには管理者の権限もしくは許可が必要です。

ですが今回はブックマークレットという方法を利用して自動で画面を操作し、過去のチャット情報を読み取る形でcsvでダウンロードする方法を紹介します。

ブックマークレットとは?

ブックマークレットは、ブラウザのブックマークとして保存できるプログラムです。 「javascript:」で始まる短いスクリプトで、ブックマークをクリックすることで任意の処理を実行できます。

ブックマークレットは便利なツールですが、悪意のあるスクリプトが含まれている可能性もあるため、信頼できるソースからのスクリプトのみを使用してください。

SANANE

実行前にAIツールなどでスクリプトを確認してもらい、外部への送信や悪意のあるダウンロードを行う処理がないか内容を確認してもらうことを推奨します!

ブックマークレットの導入方法 (Chrome)

  1. ブラウザのブックマークバーを表示する
    • Chrome右上のメニュー(⋮) > ブックマーク > ブックマークバーを表示
    • または Ctrl + Shift + B (Windows/Linux) / Command + Shift + B (Mac)
  2. 新しいブックマークを作成する
    • ブックマークバーを右クリック > 「ページを追加」を選択
  3. ブックマークの設定
    • 名前: 任意の名前(例: Workchat Export)
    • URL: 下記のJavaScriptコードをコピー&ペースト
  4. 保存をクリック
javascript:(async function(){
  var statusDiv = document.createElement('div');
  statusDiv.style.position = "fixed";
  statusDiv.style.bottom = "0";
  statusDiv.style.right = "0";
  statusDiv.style.zIndex = "9999";
  statusDiv.style.background = "rgba(0, 0, 0, 0.7)";
  statusDiv.style.color = "white";
  statusDiv.style.padding = "10px";
  statusDiv.style.fontSize = "12px";
  statusDiv.style.maxWidth = "300px";
  statusDiv.style.lineHeight = "1.2em";
  statusDiv.style.fontFamily = "Arial, sans-serif";
  statusDiv.innerHTML = "Starting...";
  document.body.appendChild(statusDiv);
   
  function updateStatus(msg) {
    statusDiv.innerHTML = msg;
  }
   
  var main = document.querySelector('[role="main"]');
  if(!main){ updateStatus("Main div not found."); return; }
  var targetDivs = main.querySelectorAll('div');
  if(targetDivs.length < 7){ updateStatus("Not enough divs in main."); return; }
  var scrollContainer = targetDivs[6];
   
  var processed = new Set();
  var csvLines = [];
  var iteration = 0;
   
  while(true){
    iteration++;
    updateStatus("Iteration: " + iteration + " - Checking for unprocessed chats...");
    var containers = Array.from(document.querySelectorAll('div[data-virtualized="false"]'));
    var unprocessed = containers.filter(el => !processed.has(el));
    if(unprocessed.length > 0){
      unprocessed.sort((a, b) => b.getBoundingClientRect().top - a.getBoundingClientRect().top);
      let chatContainer = unprocessed[0];
      processed.add(chatContainer);
      let chatContent = "";
      let contentElem = chatContainer.querySelector('div[dir="auto"].html-div');
      if(contentElem){ 
        chatContent = contentElem.textContent; 
        console.log("Chat content:", chatContent);
      }
      let chatDatetime = "";
      let messagesTables = chatContainer.querySelectorAll('div[data-scope="messages_table"]');
      let messagesTable = null;
      if(messagesTables.length > 1){ messagesTable = messagesTables[1]; }
      else if(messagesTables.length === 1){ messagesTable = messagesTables[0]; }
      if(messagesTable){
        let children = messagesTable.children;
        for(let i = 0; i < children.length; i++){
          if(children[i].tagName.toUpperCase() !== "DIV") continue;
          let role = children[i].getAttribute("role");
          if(role === "presentation" || role === "none") continue;
          chatDatetime = children[i].textContent;
          console.log("Chat datetime:", chatDatetime);
          break;
        }
      }
      let userText = "";
      if(messagesTable){
        let userElem = messagesTable.querySelector('div[role="presentation"]');
        if(userElem){
          userText = userElem.textContent;
          console.log("User text:", userText);
        }
      }
      let csvLine = '"' + chatContent.replace(/"/g,'""') + '","' + chatDatetime.replace(/"/g,'""') + '","' + userText.replace(/"/g,'""') + '"';
      csvLines.push(csvLine);
      console.log("Processed chat:", csvLine);
      updateStatus("Processed chats: " + csvLines.length);
      chatContainer.scrollIntoView();
      await new Promise(r => setTimeout(r, 1000));
      continue;
    } else {
      updateStatus("No unprocessed chats found. Scrolling up...");
      let prevScrollTop = scrollContainer.scrollTop;
      scrollContainer.scrollTop = prevScrollTop - 100;
      console.log("Scrolled up. New scrollTop:", scrollContainer.scrollTop);
      await new Promise(r => setTimeout(r, 1000));
      if(scrollContainer.scrollTop <= 0 || scrollContainer.scrollTop === prevScrollTop){
        updateStatus("Reached top. Waiting for new chats...");
        let waited = 0;
        let newChatFound = false;
        while(waited < 10){
          await new Promise(r => setTimeout(r, 1000));
          waited++;
          containers = Array.from(document.querySelectorAll('div[data-virtualized="false"]'));
          unprocessed = containers.filter(el => !processed.has(el));
          if(unprocessed.length > 0){ 
            newChatFound = true; 
            updateStatus("New chats loaded after waiting " + waited + " seconds."); 
            break; 
          }
        }
        if(newChatFound){ continue; }
        else { 
          updateStatus("No new chats after waiting for 10 seconds. Finishing up..."); 
          break; 
        }
      }
    }
  }
  let csvContent = csvLines.join("\n");
  let bom = "\uFEFF";
  let blob = new Blob([bom + csvContent], {type:"text/csv;charset=utf-8"});
  let url = URL.createObjectURL(blob);
  let a = document.createElement("a");
  a.href = url;
  a.download = "chats.csv";
  a.click();
  updateStatus("CSV file downloaded. Total chats: " + csvLines.length);
  console.log("CSV file downloaded.");
})();

これで設定は完了です。

Workchatをブラウザで開き、ダウンロードを取得したいチャットを開きます。

あとは作成したブックマークをクリックすると、 スクリプトが実行されメッセージの読み込みとエクスポートが開始されます。

ブックマークレットの動作

動作の流れ:

  1. 実行すると、右下にステータスが表示され画面に表示されているチャットを取得します。
  2. 画面内のすべてのチャットを取得すると、上部までスクロールして過去メッセージを読み込みます。
  3. 全メッセージの処理完了後、csvファイルをダウンロードします。
  4. csvにはメッセージ内容、チャット日、ユーザを記録します。

注意事項

  • WebブラウザのWorkchatで実行する必要があります。
  • 取得できるのは1トークルームずつです。
  • Workplaceのチャット画面で実行してください