GASで今日のタスクをslack通知する方法【files.upload APIを使わずグラフ投稿】

チームの人数が増えてくると、各メンバーのタスク管理をするのは大変。私たちはタスクをスプレットシートで管理しているので、今回slack連携し、今日期限のタスクを各メンバーをメンションされて毎朝7:00ごろに通知する仕組みをGoogle Apps Scriptで開発しました。

また、files.upload API 廃止されたことによりグラフなどの画像のslackポストも難しくなりました。この記事ではタスク管理の仕方・通知のためのGoogle Apps Scriptのコード・files.upload APIを使わずにグラフ等の画像をポストする方法を記載します。

弊社では

日々行っている「煩わしい!」と感じる作業を、自動化することによってお客様の生産性を高める環境作りのサポート

をさせていただいております。

お客様が自動化したい内容・ご要望をヒアリングし、0から開発・運用を行います。

まずは、「無料お問い合わせ」からお気軽にご連絡ください。

 ➡︎無料で自動化について相談をしてみる

目次

最終アウトプットイメージ

本記事に書かれている内容を実施すると、このようなことができるようになります。

①毎朝7時に、今日期限のタスクを自動で抽出され、slackに通知される。

②管理しやすいようにビジュアル化され、それもslackに通知される。

③緊急度・重要度の2軸から「誰が」「どの優先度」のタスクを「いくつ」抱えているのかを散布図で見える化

スマホでもこのように見えます。

この通知を取り入れることによって、タスクの分散やメンバーの負担が見える化されました。

通知するための設定(図解で説明)

今回、使用するスプレットシートはこちらになります。

https://docs.google.com/spreadsheets/d/1wRxD-sO7TgCYwBgi3oNwfPMQl4h-BseMY_XZkLmVzmQ/edit?gid=0#gid=0

Step1:まず最初に基本情報を入力する(完成率:33%)

「最初に埋める」シートに3つの基本情報を入力します。

前処理として、編集できるようにスプレットシートのコピーを作成します。

①「最初に埋める」シートに3つの基本情報を入力します。添付の画像にも書いていますが、タスクのジャンルを記入します。例えば具体的なタスクを抽象度高くしたときに何に関連するものなのかと考えるとわかりやすいと思います。例えば「マーケティングに関する内容」や「客単価を挙げる施策に関する内容」などの粒度で問題ないです。

②次にタスクを管理したいメンバーの名前をここに記入します。

③各メンバーの名前を記入できたら、そのメンバーに付随するslackのメンバーIDを記入しましょう。slackのメンバーIDの取得方法がわからない方も多いと思うので取得方法を下に記載しておきます。

slackの画面に行き、メンバーIDを知りたいメンバーのプロフィールを確認します。プロフィールの見方は、その人の名前の上にマウスのカーソルを持っていき右クリック > 会話の詳細を表示 > プロフィール全体を表示する で表示することができます。

表示することができたら、縦に3つ並んでいる丸のアイコンをクリックします。(以下の画像の赤枠で囲われている部分になります。)

「メンバーIDをコピー」が表示されるのでクリックします。

このようにできたらOK!

Step2:実際にタスクを記入していく(完成率:66%)

それでは実際に管理したいタスクを入力していきましょう。

このタスクを記入することがこの管理のすべてと言っても過言ではありません。各項目に沿ってタスクの内容を記入していくだけです。各プルダウンになっている箇所は前工程で記入した項目から選択できるようになっています。何も難しいことはないのですが、J列とK列だけ「yyyy/mm/dd」の形で日付を入力するように注意してください。日付データだと認識されなかったり、年を間違えていると他の関数がうまく拾ってくれないというケースもあります。

またもう一つ注意事項があります。L列〜O列は削除しないようにしてください。

これは今日から期限日まで何営業日あるのかを自動で算出するための関数を入力しています。

タスクは一度入力したら終わりではなく、タスクが発生したら都度追加する形で入力してください。

Step3:自分のslackに通知されるように、Google Apps Scriptを修正する(完成率:100%)

最後に、自分のslackワークスペースに通知されるように、Google Apps Scriptを変更しましょう。

コードは以下の通りです。

function main(){
  sendTodayTasksToSlack();
  postChartsWithPreviewToSlack();
}

function sendTodayTasksToSlack() {
  const slackWebhookUrl = 'https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxx'; // 御社のWebhook URLを入力

  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const taskSheet = ss.getSheetByName('データ整形');
  const nameSheet = ss.getSheetByName('最初に埋める');

  const taskData = taskSheet.getDataRange().getValues();
  const nameData = nameSheet.getDataRange().getValues();

  postToSlack(slackWebhookUrl, '今日期限のタスクは以下の通りです。');

  for (let i = 4; i < taskData.length; i++) {
    const row = taskData[i];
    const taskId = row[29]; // AD列

    if (!taskId) continue;

    const genre = row[32];      // AG列
    const taskName = row[30];   // AE列
    const status = row[31];     // AF列
    const tantouName = row[33]; // AH列
    const dcName = row[34];     // AI列

    const startDate = convertToDateString(row[35]); // AJ列
    const deadline = convertToDateString(row[36]);  // AK列

    const priority = row[38];   // AM列
    const notes = row[39];      // AN列

    const tantouMention = getSlackMentionName(tantouName, nameData);
    const dcMention = getSlackMentionName(dcName, nameData);

    const message = `
担当者:<@${tantouMention}>
ダブルチェック:<@${dcMention}>

\`\`\`
タスクID:${taskId}
ジャンル:${genre}
タスク名:${taskName}
ステータス:${status}
担当者:${tantouName}
ダブルチェック担当者:${dcName}
着手日:${startDate}
期限日:${deadline}
重要度:${priority}

備考
${notes}
\`\`\`
`;
    postToSlack(slackWebhookUrl, message.trim());
  }
}

function getSlackMentionName(personName, nameData) {
  for (let i = 0; i < nameData.length; i++) {
    if (nameData[i][7] === personName) {
      return nameData[i][8] || personName;
    }
  }
  return personName;
}

function convertToDateString(cellValue) {
  if (cellValue instanceof Date) {
    return Utilities.formatDate(cellValue, Session.getScriptTimeZone(), 'yyyy/MM/dd');
  } else if (typeof cellValue === 'number') {
    const jsDate = new Date(1899, 11, 30 + cellValue); // 注意: ローカルタイムでOK
    return Utilities.formatDate(jsDate, Session.getScriptTimeZone(), 'yyyy/MM/dd');
  } else {
    return cellValue;
  }
}

function postToSlack(webhookUrl, message) {
  const payload = JSON.stringify({ text: message });

  UrlFetchApp.fetch(webhookUrl, {
    method: 'post',
    contentType: 'application/json',
    payload: payload,
  });
}





const IMGUR_CLIENT_ID = 'xxxxxxxxxxxxxxxx';  // Imgurから取得したClient-ID
const SLACK_WEBHOOK_URL = 'https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxx'; // 御社のWebhook URLを入力

function postChartsWithPreviewToSlack() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('graph');
  const charts = sheet.getCharts();

  charts.forEach((chart, index) => {
    const blob = chart.getAs('image/png').setName(`chart_${index + 1}.png`);

    // Imgurに画像アップロード
    const imgurUrl = uploadImageToImgur(blob);
    if (!imgurUrl) {
      Logger.log(`グラフ${index + 1}のImgurアップロードに失敗しました`);
      return;
    }

    // Slackに送信(画像URL付き)
    const slackMessage = {
        text: imgurUrl  

    };

    UrlFetchApp.fetch(SLACK_WEBHOOK_URL, {
      method: 'post',
      contentType: 'application/json',
      payload: JSON.stringify(slackMessage),
    });
  });
}

function uploadImageToImgur(blob) {
  try {
    const encoded = Utilities.base64Encode(blob.getBytes());
    const options = {
      method: 'post',
      headers: {
        Authorization: 'Client-ID ' + IMGUR_CLIENT_ID,
      },
      payload: {
        image: encoded,
        type: 'base64',
      },
      muteHttpExceptions: true,
    };

    const response = UrlFetchApp.fetch('https://api.imgur.com/3/image', options);
    const result = JSON.parse(response.getContentText());

    if (result.success) {
      return result.data.link; 
    } else {
      Logger.log('Imgurエラー: ' + JSON.stringify(result));
      return null;
    }
  } catch (e) {
    Logger.log('Imgurアップロード失敗: ' + e.message);
    return null;
  }
}

まずはslackに通知したいチャネルを作成します。

チャネルの名前はどんなものでも問題ありません。

チャネルが作成できたら、Google Apps Scriptの修正をしていきましょう。

メニューバーにある 拡張機能 > Apps Script を押下します。

下のような画面が表示されます。まずは、この7行目を修正します。

slackから取得できるあなたのチーム専用のwebhook URLというものを発行する必要があります。まずは、このURL(https://api.slack.com/)にアクセスし、右上の「Your apps」をクリックします。

「Create New App」を押下する。

「From scratch」を押下する。

「App Name」と「自分のワークスペース」を選択します。

サイドメニューから「App Home」を選択。

「App Display Name」の「Edit」を押下し、入力します。入力したら「Save」を押下する。

サイドメニューから「Incomming Webhooks」を選択。 Off表示になっているのでOnに変更する。

下にスクロールして「Add New Webhook to Workspace」を押下する。

下のような画面になるので投稿したいチャネルを選択します。稀にチャネルが選択肢にないことがあるので、その場合は、チャネル名を入力すると選択肢に表示されます。

チャネルを選択できたら、「許可する」を押下する。

「Incomming Webhooks」画面に戻って来るので下にスクロールすると、Webhook URLが発行されている。この「Copy」を押下して、コピーする。

先ほどのGoogle Apps Scriptの7行目と 97行目を先ほど発行したwebhook urlに書き換える。

最後にImgur クライアントIDの取得し、Google Apps Scriptに記入します。
https://api.imgur.com/oauth2/addclient にアクセスします。アカウントを持っていない場合、以下の画面になるので、「Continue with Google」を押してアカウント作成を進めましょう。

https://api.imgur.com/oauth2/addclient にアクセスし、以下のように記入し、「submit」を押下する。

Application name: アプリケーション名
Application name: 匿名アップロードだけできれば良い場合はAnonymous usage without user authorizationを選択
Authorization callback URL: 匿名アップロードの場合は関係ない。適当なURLを入力しておく。
Application website (optional): 省略可
Email: 適当なメールアドレス
Description: 省略可

参照:https://qiita.com/shin27/items/b715736bf7fb5beebdab

submitできたら「Client-Id」を取得できるので、そのClient-IdをGoogle Apps Scriptの96行目に入力します。

最後に毎日定期的な時間に自動で通知されるようにトリガーを設置します。

サイドメニューからトリガーを選択し、右下にある「トリガーを追加」を押下します。

イベントのソースを選択を「時間手動型」、時間ベースのトリガーのタイプを選択を「日付ベースのタイマー」、時刻を選択を通知したい時間帯に設定し、保存を押下する。

初回だと以下のような画像が表示されるので自分のアカウントを選択する。

「Advanced」もしくは「詳細」をクリックする。

詳細が表示されるので、隠れていたリンクをクリックする。

下までスクロールします。

「Allow」もしくは「許可」を押下。

先ほどのトリガー追加の画面になり、トリガーが下のように追加されていたら完了になります。

お気軽にお問い合わせください

Google Apps Scriptだけではなく、生成AIやVBA、RPAなどを使って、業務の自動化をしております。

お客様が自動化したい内容・ご要望をヒアリングし、0から開発・運用を行います。

Slackの自動通知や業務効率化、自動化などの関するお悩みの相談も承っております。少しでも興味をお持ちいただいた方は下記からお気軽にご連絡ください!

 ➡︎無料で自動化について相談をしてみる

目次