【ChatGPT×Slack】SlackチャンネルにChatGPTを召喚!ChatGPT APIを使った要約アプリの作り方

この記事は、ChatGPT APIを活用してSlack チャンネルの文章を要約する方法を紹介します。
Slackの要約機能は、執筆した3/14現在では、Salesforceのベータ版がありますが、申請が必要です。
プログラムを実行するまでの手順も解説してますので、最後までお読みいただけますとっ!
今回作った要約Bot
チャンネル内の会話を要約するために、ChatGPT APIを活用したSlackアプリを作りました。
要約された様子は以下のようになります。

どんなものかと簡単に申し上げますと
- (弊社環境では)times チャンネルに、対象チャンネルの要約がリストされる
- 対象になるチャンネルは25時間以内に会話があったパブリック/プライベートチャンネル
です。
アプリ開発の際、参考にした記事はこちらです。
https://note.com/masuidrive/n/na0ebf8a4c4f0
パブリックチャンネルのみ対象のものでしたので、プライベートチャンネルも対象になるよう修正しております。
実行するプログラムを公開
プライベートチャンネルでも利用いただけるようにしたプログラムはこちらです。
実行するための設定は、このあと写真もりもりで解説します。
#!/usr/bin/env python3
# https://github.com/masuidrive/slack-summarizer
# by [masuidrive](https://twitter.com/masuidrive) @ [Bloom&Co., Inc.](https://www.bloom-and-co.com/) 2023- [APACHE LICENSE, 2.0](https://www.apache.org/licenses/LICENSE-2.0)
import os
import re
import time
import pytz
from slack_sdk.errors import SlackApiError
from slack_sdk import WebClient
from datetime import datetime, timedelta
import openai
from openai.error import InvalidRequestError
#以下の3つはご自身の環境で取得し書き換える必要があります。
openai.api_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# APIトークンとチャンネルIDを設定する
TOKEN = "xoxb-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
CHANNEL_ID = "XXXXXXXXX"
# OpenAIのAPIを使って要約を行う
def summarize(text):
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
temperature=0.5,
messages=[
{"role": "system", "content": "チャットログのフォーマットは発言者: 本文\\nになっている。\\nは改行を表しています。これを踏まえて指示に従います"},
{"role": "user", "content": f"下記のチャットログを箇条書きで要約してください。1行ずつの説明ではありません。全体として短く。\n\n{text}"}
]
)
return response["choices"][0]["message"]['content']
# 取得する期間を計算する
HOURS_BACK = 25
JST = pytz.timezone('Asia/Tokyo')
now = datetime.now(JST)
yesterday = now - timedelta(hours=HOURS_BACK)
start_time = datetime(yesterday.year, yesterday.month, yesterday.day,
yesterday.hour, yesterday.minute, yesterday.second)
end_time = datetime(now.year, now.month, now.day,
now.hour, now.minute, now.second)
# Slack APIクライアントを初期化する
client = WebClient(token=TOKEN)
# ユーザーIDからユーザー名に変換するために、ユーザー情報を取得する
try:
users_info = client.users_list()
users = users_info['members']
except SlackApiError as e:
print("Error : {}".format(e))
exit(1)
# チャンネルIDからチャンネル名に変換するために、チャンネル情報を取得する
try:
channels_info = client.conversations_list(
types="public_channel,private_channel",
exclude_archived=False,
limit=200
)
channels = [channel for channel in channels_info['channels']
if not channel["is_archived"] and channel["is_channel"]]
channels = sorted(channels, key=lambda x: int(re.findall(
r'\d+', x["name"])[0]) if re.findall(r'\d+', x["name"]) else float('inf'))
except SlackApiError as e:
print("Error : {}".format(e))
exit(1)
# 指定したチャンネルの履歴を取得する
def load_messages(channel_id, ts):
result = None
result = client.conversations_replies(ts=ts,
channel=channel_id,
oldest=start_time.timestamp(),
latest=end_time.timestamp()
)
# messages = result["messages"]
messages = list(filter(lambda m: "subtype" not in m, result["messages"]))
if len(messages) < 1:
return None
messages_text = []
while result["has_more"]:
result = client.conversations_replies(ts=ts,
channel=channel_id,
oldest=start_time.timestamp(),
latest=end_time.timestamp(),
cursor=result["response_metadata"]["next_cursor"]
)
messages.extend(result["messages"])
for message in messages[::-1]:
if "bot_id" in message:
continue
if message["text"].strip() == '':
continue
# ユーザーIDからユーザー名に変換する
user_id = message['user']
sender_name = None
for user in users:
if user['id'] == user_id:
sender_name = user['name']
break
if sender_name is None:
sender_name = user_id
# テキスト取り出し
text = message["text"].replace("\n", "\\n")
# メッセージ中に含まれるユーザーIDやチャンネルIDを名前やチャンネル名に展開する
matches = re.findall(r"<@[A-Z0-9]+>", text)
for match in matches:
user_id = match[2:-1]
user_name = None
for user in users:
if user['id'] == user_id:
user_name = user['name']
break
if user_name is None:
user_name = user_id
text = text.replace(match, f"@{user_name} ")
matches = re.findall(r"<#[A-Z0-9]+>", text)
for match in matches:
channel_id = match[2:-1]
channel_name = None
for channel in channels:
if channel['id'] == channel_id:
channel_name = channel['name']
break
if channel_name is None:
channel_name = channel_id
text = text.replace(match, f"#{channel_name} ")
messages_text.append(f"{sender_name}: {text}")
if len(messages_text) == 0:
return None
else:
return messages_text
result_text = []
for channel in channels:
try:
threads = client.conversations_history(channel=channel["id"],
oldest=start_time.timestamp(),
latest=end_time.timestamp())
except SlackApiError as e:
if e.response['error'] == 'not_in_channel':
response = client.conversations_join(
channel=channel["id"]
)
if not response["ok"]:
raise SlackApiError("conversations_join() failed")
time.sleep(5) # チャンネルにjoinした後、少し待つ
threads = client.conversations_history(
channel=channel["id"],
oldest=start_time.timestamp(),
latest=end_time.timestamp()
)
else:
print("Error : {}".format(e))
continue
messages_in_channel = []
if not threads["messages"]:
continue
result_text.append(f"----\n<#{channel['id']}>\n")
for thread in threads["messages"]:
if thread:
messages = load_messages(channel["id"], thread["ts"])
if messages:
messages_in_channel.extend(messages[::-1])
try:
result_text.append(summarize(messages_in_channel))
except InvalidRequestError as e:
result_text.append('4096tokenを超えたため生成に失敗しました')
title = (f"{yesterday.strftime('%Y-%m-%d')}の要約")
text = title+"\n\n"+"\n\n".join(result_text)
response = client.chat_postMessage(
channel=CHANNEL_ID,
text=text
)
print("Message posted: ", response["ts"])
プログラムを実行する手順
プログラムを実行するとき、定期実行と都度実行の2パターンがあります。
今回は、都度実行するための手順を紹介します。
実行する前準備として、以下3つの変数に格納している値を書き換えます。
- openai.api_key
- TOKEN
- CHANNEL_ID
プログラムではここですね。
#以下の3つはご自身の環境で取得し書き換える必要があります。
openai.api_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# APIトークンとチャンネルIDを設定する
TOKEN = "xoxb-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
CHANNEL_ID = "XXXXXXXXX"
こちらは、みなさんの環境独自のデータですので、それぞれ取得していきましょう。
OpenAI APIキーを取得する
openai.api_key は、OpenAI API キーを格納する変数です。
まずはOpenAI の Web サイト アクセスします。
適宜、Sign In して、自身のアカウントアイコン→View API Keysをクリック

「Create new secret key」をクリックする

赤枠内にあるシークレットキーをコピー

OpenAI APIキーの取得手順は以上です。
Slack APIキーを取得する
TOKENは、Slack APIキーを格納している変数です。
Slack API の Web サイトにアクセス。ログインし「Create an app」をクリック

「From an app manifest 」をクリック


以下のjsonを貼り付けて「Next」をクリック
{"display_information":{"name":"Summary","description":"Public channelのサマリーを作る","background_color":"#d45f00"},"features":{"bot_user":{"display_name":"Summary","always_online":false}},"oauth_config":{"scopes":{"bot":["channels:history","channels:join","channels:read","chat:write","users:read"]}},"settings":{"org_deploy_enabled":true,"socket_mode_enabled":false,"token_rotation_enabled":false}}

「Install to Workspace」をクリック

「許可する」をクリックし、画面遷移するまで待つ

左側の「OAuth & Permissions 」をクリックすると、「xoxb-」から始まるAPI キーが取得できる。

Slack APIキーの取得手順は以上です。
チャンネルIDを取得する
CHANNEL IDは、アプリを追加したいチャンネルIDを格納している変数です。
チャンネル名をクリックすると、下に表示されます

これで準備完了です。プログラムの該当部分を書き換えてください。
アプリを追加する
「アプリを追加する」 から「timesチャンネル」にSummaryアプリを召喚。

Pythonプログラムを実行すると……(添付の画像は、実行環境の一例です)

各チャンネルの内容が要約されます!

以上で、ChatGPT APIを使ったSlack要約アプリの手順は終了です。
ぜひご活用ください。
\ご覧いただきありがとうございます!/
チャットボット開発等のPoC開発
承っております!

- “ChatGPT × 社内データ“のAIチャットボットを作りたい方
- 自社の業務にジェネレーティブAIを適用したい方

Recruit
現在、生成系AI事業急成長のため
積極的に人材採用を行なっています
ChatGPT講演会承っております!
