見出し画像

TableauのVizをSlackに自動配信する方法(複数画像対応)

主に SlackでTableau Vizを自動配信:データへの関心を高めるため をほとんど参考にして進めました。
(SlackでVizを自動配信する具体的な手順や設定方法などは、上記の記事でしっかり解説されているので、そちらを中心にご参照ください。)

今回は、複数枚のVizをSlackに送信する方法を実践したので、その方法についてまとめたいと思います!
複数枚の画像をまとめてSlackに送信する際には下記の記事やドキュメントも参考にしました!


なぜやるのか

  • Tableauを導入したばかりで、まだライセンスを持っていないメンバーが多い

  • Slackなら誰でもすぐに確認できるので、KPIを見る習慣がつきやすい

  • テキストだけだと伝わりづらい数字も、Viz(グラフ画像)なら一目で把握できる

例えば、今までは毎日のKPIの数値を以下の感じでテキストのみ通知していましたが、
【1月売上 1000万円 目標!】
01/10時点 100,000 / 1,000,000
達成率 10%
残り日数 21日
残り金額 900,000円
2025-01-08 50,000
2025-01-09 60,000
2025-01-10 55,000

しかし、VizのスクリーンショットをSlackに自動配信するようにすると、よりパッと見で数値の状況がわかりやすくなり、チームの数値への意識も高まりました!

Slack通知サンプル

1枚目は大きく表示(メインの数値やKPIを強調)、2〜6枚目は小さく表示しつつ、クリックすると拡大できるようにしています。
複数枚をすべて大きく表示すると、Slackの画面が埋まってしまうので、このような実装にしています。

SlackのViz通知Botのイメージ

コード例(GAS)

以下のサンプルでは、GmailでTableauから届いたメールを探し、そのメール内の添付ファイルを取得してSlackに通知しています。
2枚目の添付ファイルをメインの画像(大きく表示)に、3〜7枚目の画像を小さく並べ、クリックするとDriveで拡大表示できるようにしています。

function gmailToSlackWebhook() {
  try {
    // (A) 検索クエリ:Tableauからのメール例
    var searchQuery = 'from:"no-reply@tableau.com" subject:"KPIダッシュボード"';
    // Gmailから最新スレッドを1件取得
    var threads = GmailApp.search(searchQuery, 0, 1);
    console.log("Found email threads:", threads.length);

    if (threads.length === 0) {
      throw new Error("該当するメールが見つかりませんでした。");
    }

    // スレッド内で一番新しいメッセージを取得
    var messages = threads[0].getMessages();
    var message = messages[messages.length - 1];
    console.log("Messages in thread:", messages.length);

    // 添付ファイルを取得
    var attachments = message.getAttachments();
    console.log("Attachments found:", attachments.length);

    if (attachments.length < 2) {
      throw new Error("メールに2つ以上の添付がありません。");
    }

    // Slack Webhook (Incoming Webhook) を使った投稿
    var webhookUrl = "https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX"; // 実際のWebhook URLに変更

    // JSONデータの構築
    var blocks = [
      {
        "type": "section",
        "text": {
          "type": "mrkdwn",
          "text": "*昨日までの実績*"
        }
      }
    ];

    // 1枚目の画像を大きく表示(メインViz)
    var secondAttachment = attachments[1];
    var secondFile = DriveApp.createFile(secondAttachment.copyBlob());
    secondFile.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
    var secondImageUrl = "https://drive.google.com/uc?export=view&id=" + secondFile.getId();
    
    blocks.push({
      "type": "image",
      "title": {
        "type": "plain_text",
        "text": secondAttachment.getName()
      },
      "image_url": secondImageUrl,
      "alt_text": secondAttachment.getName()
    });

    // 2〜6枚目の画像をcontextで表示(サムネイル+リンク)
    var contextElements = [];
    
    for (var i = 2; i < Math.min(attachments.length, 7); i++) {
      var attachment = attachments[i];
      var file = DriveApp.createFile(attachment.copyBlob());
      file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
      var imageUrl = "https://drive.google.com/uc?export=view&id=" + file.getId();
      var driveUrl = file.getUrl();
      
      var fileName = attachment.getName()
        .replace(/\.[^/.]+$/, "") // 拡張子を除去
        .replace(/\s+/g, "");     // 空白を除去
      
      // サムネイル画像を追加
      contextElements.push({
        "type": "image",
        "image_url": imageUrl,
        "alt_text": attachment.getName()
      });
      
      // クリック可能なリンク(拡大表示用)を追加
      contextElements.push({
        "type": "mrkdwn",
        "text": `<${driveUrl}|${fileName}:拡大>`
      });
    }

    if (contextElements.length > 0) {
      blocks.push({
        "type": "context",
        "elements": contextElements
      });
    }

    var jsonData = {
      "username": "昨日までの実績お知らせTableauBot",
      "blocks": blocks
    };

    // JSON文字列に変換
    var payload = JSON.stringify(jsonData);

    // リクエスト設定
    var options = {
      "method": "POST",
      "contentType": "application/json",
      "payload": payload,
      "muteHttpExceptions": true
    };

    // Slackに送信
    var response = UrlFetchApp.fetch(webhookUrl, options);
    console.log("Webhook response code:", response.getResponseCode());
    console.log("Response text:", response.getContentText());

    console.log("Slack送信を完了しました。");
  } catch (e) {
    console.error("エラー発生:", e.toString());
    throw e;
  }
}

注意点

  1. Slackアプリ作成後、Slackチャンネルで表示されるまで時間がかかる

    • Slackアプリ作成後、1時間前後反映まで時間がかかりました。

  2. Incoming WebhookでURLを発行

    • UrlFetchApp.fetch()でPOSTするときに必要

  3. 1枚目のみ大きい画像、2〜6枚目はサムネイル+拡大リンク

    • 全部大きく表示するとSlackでかなりスペースを取ってしまうので、メインVizだけ大きくして残りは小さく並べる工夫をしています。

おわりに

  • Tableauから自動送信されるVizをSlackに流すだけで、チーム全体のKPI意識が高まります!

  • まずは数値を「見える化」して、「頻繁に目に触れる場所」に置くことが大切!

  • ライセンスなしユーザーもSlackで見られるので、まずはこの方法で大枠の数値を共有してから興味を持った人にライセンスを与える...という運用もアリかなと思います!

いいなと思ったら応援しよう!