見出し画像

chatGPTでSlackのチャンネル内のユーザーの報告から日報を自動生成する[GAS]

ビジネスチームが日々抱える課題の1つに、チーム全体で情報共有することの難しさがあります。
すべての情報を把握するのは簡単なことではありません。
今回は報告をユーザーごとにまとめてchatGPTに報告してもらうことで日報を行動ベースで自動化します。chatGPTはAPIはデータの再学習しない。という改定がされたので実運用を考えてみます。*Azure版もそうなのですが、申請したら落とされました。なぜだったのか・・・

日報書くのは大変。書いてもらうのはもっと大変。

日報自体には様々な効果があります。ただ日報を毎日上司やチームメイトがわかるかたちで書くというのは難易度が高いことですよね。かといって日報を重視しすぎると日報を書く時間をとても長くかける人もいたりします。
なので弊社では日報は負担にならないように
・やったこと
・やること
・ひとこと
のフォーマットにしています。このシンプルフォーマットでも守ってもらうのは大変です。人によっては独自フォーマットにします。

そこで投稿情報から作るようにしました。

架空の報告と要約


この日報の良いところは自分の知りたい意図をプロンプトで指示できるところです。
例えば
・顧客名をいれてほしい
・やったことを箇条書きでいれてほしい
とかですね。

今回は以下のプロンプトにしました。

下記のinputはチャットメッセージでの1日の活動記録をユーザーごとにまとめた。
#以下テンプレートにしたがってoutputを出すこと。一度outputを確認してテンプレートに従っていない場合、やり直すこと。
以下テンプレート
-本日やったこと
-明日以降やること
以上テンプレート
#要件
-各項目に箇条書きで要約。
-他の項目は追加しない。
-箇条書きに数字はつけない。
-日付の記載がある場合、記載すること
-顧客名がある場合記載すること

トリガーはGASのUIから設定してください。Slackアプリの作成や権限は事前に必要です。Scopesで結構ハマるのでスクショを貼ります。

ここで意外とはまる


コードです。
やってみてわかったのは内容が安定しないので、完全な自動化は難しく、トリガーを17時までにして、AIの投稿内容をコピペして人が直して提出が良いかなと考えています。
コメントはchatGPTに考えてもらいました。全部貼ると逆にわかりづらい?

// Slackのメッセージを使って日報を生成する処理を自動化するGoogle Apps Scriptです。メイン関数のPostDailyReview()は、Slackチャンネルから指定した日付範囲のメッセージを取得し、ユーザーごとにグループ化して、各ユーザーのメッセージをChatGPT(AIモデル)に送り、ユーザーごとの日報を生成して、指定したSlackチャンネルにポストします。

// スクリプトには、getStartAndEndForCron()、convertToUnixTime()、getMessages()、groupMessagesByUser()、getUserNameFromUserId()、postToSlack()、及び callWebApi() のいくつかのヘルパー関数もあります。

// getStartAndEndForCron() は、現在の日付を取得し、日次レポートの時間範囲を設定します。 convertToUnixTime() は、日付範囲を Slack API で必要とされる Unix タイムスタンプに変換します。getUserNameFromUserId() IDからユーザー名を取得します。 postToSlack() メッセージをSlackチャンネルに投稿します。

// getChatGptMessage()は、ChatGPTからAIが生成したレポートを取得する別のヘルパー関数で、PostDailyReview()が各ユーザーの日報を生成するために使用します。

// 全体として、このスクリプトは日報の生成プロセスを自動化し、時間を節約して効率を高めるのに役立ちます。



//環境変数の設定です。
const BOT_TOKEN = PropertiesService.getScriptProperties().getProperty("SLACK_BOT_TOKEN");
const OPEN_AI_API_KEYS = PropertiesService.getScriptProperties().getProperty("OPEN_AI_API_KEYS");

// 関数PostDailyReviewは、日報を自動生成するために使用されます。
// この関数は、Slackのメッセージを取得し、ユーザーごとに分類します。
// その後、各ユーザーのメッセージをChatGPTに入力し、AIが生成した日報を取得します。
// 最後に、AIが生成した日報を指定されたSlackチャンネルに投稿します。
// この関数では、getStartAndEndForCron()、convertToUnixTime()、getMessages()、groupMessagesByUser()、getUserNameFromUserId()、getChatGptMessage()、postToSlack()の関数を使用します。
// 具体的には、getStartAndEndForCron()を使用して、投稿を取得する日付範囲を決定し、convertToUnixTime()を使用してSlack APIに必要なタイムスタンプ形式に変換します。
// その後、getMessages()を使用して、指定されたチャンネルのメッセージを取得し、groupMessagesByUser()を使用して、ユーザーごとに分類します。
// ユーザーごとにChatGPTに入力し、生成された日報を取得し、最後にSlackチャンネルに投稿します。

function PostDailyReview() {
  messageSourceChannel = ""
  slackMessageChannel = ""
  const { start, end } = getStartAndEndForCron();
  const { startSlackTimestamp, endSlackTimestamp } = convertToUnixTime(start, end)
  const messages = getMessages(messageSourceChannel, startSlackTimestamp, endSlackTimestamp)
  const userMessages = groupMessagesByUser(messages)
  const behavior = `下記のinputはチャットメッセージでの1日の活動記録をユーザーごとにまとめた。
#以下テンプレートにしたがってoutputを出すこと。一度outputを確認してテンプレートに従っていない場合、やり直すこと。
以下テンプレート
-本日やったこと
-明日以降やること
以上テンプレート
#要件
-各項目に箇条書きで要約。
-他の項目は追加しない。
-箇条書きに数字はつけない。
-日付の記載がある場合、記載すること
-顧客名がある場合記載すること`
  slackMessages = ""
  for (const user in userMessages) {
    //botなど日報にいれたくないbot_idを入れてください。
    if (user === 'U0-----') {
      // U0------の場合、何もしない。
      continue;
    }
    userName = getUserNameFromUserId(user)
    result = `${userMessages[user].join(',')}`;
    chatGPTMessages = getChatGptMessage(`${behavior}
    input:${result}
    `)
    slackMessages += `AI日報---${start}-${end}\n${userName}-<@${user}>\n${chatGPTMessages}\n\n`
    console.log(result)
  }
  postToSlack(slackMessages, slackMessageChannel)

}

// 関数getStartAndEndForCronは、現在の日付を取得し、指定されたフォーマットに整形します。
// その後、日報の範囲の07:00:00から16:00:00までの範囲を、現在の日付に含めた日付時刻形式で取得します。
// 最後に、startとendをオブジェクト形式で返します。この関数は、Cronジョブの実行時間範囲を指定するために使用されます。
function getStartAndEndForCron() {
  const today = new Date();
  const year = today.getFullYear();
  const month = String(today.getMonth() + 1).padStart(2, '0');
  const day = String(today.getDate()).padStart(2, '0');
  const dateString = `${year}-${month}-${day}`;

  const start = `${dateString} 07:00:00`;
  const end = `${dateString} 16:00:00`;

  return { start, end };
}

//usernameから名前を取得します。
function getUserNameFromUserId(userId) {
  // Slack APIのURLを設定
  const apiResponse = callWebApi(BOT_TOKEN, "users.info", {
    token: BOT_TOKEN,
    user: userId
  });
  response = JSON.parse(apiResponse)
  return response.user.real_name
}

//{userA:[投稿日時-テキスト,投稿日時-テキスト],userB:[投稿日時-テキスト]}という形式にします。
//  例 {userId: [ '2023-03-08 08:35:34 - 昨日の顧客との商談のメモを#salesチャンネルに投稿しましたので、チェックしてください。}',
// 関数groupMessagesByUserは、Slackの投稿情報をユーザーごとに分類するために使用されます。
// この関数は、投稿情報の配列を受け取り、users配列を作成します。users配列には、投稿情報に含まれるユーザーの一意なリストが含まれます。
// 次に、messagesByUserオブジェクトを作成し、各ユーザーについて投稿情報をフィルタリングしてmessagesByUserオブジェクトに追加します。
// この関数では、filter()メソッドを使用して、メッセージオブジェクトをフィルタリングし、user属性が現在のユーザーに一致するものだけを含めます。
// 次に、map()メソッドを使用して、各メッセージオブジェクトをJSTの日付時刻形式にフォーマットして、messagesByUserオブジェクトに追加します。
// 最後に、messagesByUserオブジェクトを返します。
function groupMessagesByUser(objects) {
  const users = [...new Set(objects.map(obj => obj.user))];
  const messagesByUser = {};

  users.forEach(user => {
    messagesByUser[user] = objects
      .filter(obj => obj.type === 'message' && obj.user === user)
      .map(obj => {
        const sendtime = Utilities.formatDate(new Date(obj.ts * 1000), "JST", "yyyy-MM-dd HH:mm:ss");
        return `${sendtime} - ${obj.text}`;
      });
  });
  return messagesByUser;
}

// 関数convertToUnixTimeは、2つの日付をUNIXタイムスタンプ形式に変換します。
// この関数は、Slack APIに必要なタイムスタンプ形式に変換するために使用されます。
// まず、startDateとendDateをDate型に変換し、toLocaleString()メソッドを使用して文字列形式に変換します。
// 次に、getTime()メソッドを使用して、各日付をUNIXタイムスタンプに変換します。
// 最後に、Slack APIで使用されるタイムスタンプ形式に変換し、startSlackTimestampとendSlackTimestampとして返します。

function convertToUnixTime(startDate, endDate) {
  startDate = new Date(startDate).toLocaleString();
  endDate = new Date(endDate).toLocaleString();
  var startTimeUnix = Math.floor(new Date(startDate).getTime() / 1000);
  var endTimeUnix = Math.floor(new Date(endDate).getTime() / 1000);
  const startSlackTimestamp = startTimeUnix + '.000000';
  const endSlackTimestamp = endTimeUnix + '.000000';
  return { startSlackTimestamp, endSlackTimestamp };
}


// 関数getMessagesは、Slackチャンネルから指定された期間の投稿情報を取得するために使用されます。
// この関数は、channel、startDate、endDateの3つのパラメータを受け取ります。
// channelは、投稿情報を取得するチャンネルのIDまたは名前です。
// startDateは、取得する投稿情報の開始日時です。
// endDateは、取得する投稿情報の終了日時です。
// 関数は、callWebApi()を使用して、Slack APIを呼び出し、指定された期間の投稿情報を取得します。
// 最後に、APIから取得した投稿情報を配列で返します。
function getMessages(channel, startDate, endDate) {
  // Slack APIのURLを設定
  const apiResponse = callWebApi(BOT_TOKEN, "conversations.history", {
    token: BOT_TOKEN,
    channel: channel,
    oldest: startDate,
    latest: endDate
  });
  response = JSON.parse(apiResponse)
  return response.messages
}

// 関数postToSlackは、Slackチャンネルにメッセージを投稿するために使用されます。
// この関数は、messageとchannelの2つのパラメータを受け取ります。
// messageは、投稿するメッセージの内容です。
// channelは、メッセージを投稿するチャンネルのIDまたは名前です。
// 関数は、callWebApi()を使用して、Slack APIを呼び出し、指定されたチャンネルにメッセージを投稿します。
// 関数は、戻り値を返さず、単にAPIのレスポンスを解析しています。

function postToSlack(message, channel) {
  const apiResponse = callWebApi(BOT_TOKEN, "chat.postMessage", {
    channel: channel,
    text: message,
  })
  response = JSON.parse(apiResponse)
}

// 関数callWebApiは、SlackのWeb APIを呼び出すために使用されます。
// この関数は、BOT_TOKEN、apiMethod、payloadの3つのパラメータを受け取ります。
// BOT_TOKENは、Slack APIにアクセスするために必要な認証トークンです。
// apiMethodは、呼び出すAPIのメソッド名です。
// payloadは、APIに渡すパラメータのオブジェクトです。
// UrlFetchApp.fetch()メソッドを使用して、Slack APIを呼び出し、レスポンスを取得します。
// レスポンスは、Web APIの応答を表すオブジェクトであり、関数の戻り値として返されます。
// 関数の最後に、レスポンスをログに出力しています。

function callWebApi(BOT_TOKEN, apiMethod, payload) {
  const response = UrlFetchApp.fetch(
    `https://www.slack.com/api/${apiMethod}`,
    {
      method: "post",
      contentType: "application/x-www-form-urlencoded",
      headers: { "Authorization": `Bearer ${BOT_TOKEN}` },
      payload: payload,
    }
  );
  console.log(`Web API (${apiMethod}) response: ${response}`)
  return response;
}

// ChatGPTのAPIを呼び出し応答を取得する
function getChatGptMessage(message) {
  const uri = 'https://api.openai.com/v1/chat/completions';

  const headers = {
    'Authorization': `Bearer ${OPEN_AI_API_KEYS}`,
    'Content-type': 'application/json',
    'X-Slack-No-Retry': 1
  };

  const options = {
    'muteHttpExceptions': true,
    'headers': headers,
    'method': 'POST',
    'payload': JSON.stringify({
      "model": "gpt-3.5-turbo",
      "max_tokens": 1024,
      "messages": [{ "role": "user", "content": (message) }]
    })
  };
  try {
    const response = UrlFetchApp.fetch(uri, options);
    const json = JSON.parse(response.getContentText());
    console.log(json["usage"]);
    return json["choices"][0]["message"]["content"];
  } catch (e) {
    console.log('error');
  }
}


介護、障がい職員募集しています。是非。


この記事が気に入ったらサポートをしてみませんか?