はじめに
みなさん、こんにちはー。Shizen ConnectのCEOの松村です。
Slackのメッセージを後から検索したり、AIで要約させたい場面は多いですが、Slack標準の検索機能だけでは限界を感じることがありますよね。
で、思い立って Google Apps Script (GAS) を利用して、Slackの過去ログ全件と、1時間ごとの新規投稿を自動でGoogle Driveにテキスト保存する仕組みを構築しました。
もちろん色々な実現方法があるのですが、結構コストが無視できなくて……。
Google Drive上のファイルをGeminiに読み込ませることで、高精度な検索・要約が可能な環境となり、個人的にはめちゃくちゃ満足です。しかも無料です!
1. Slack Appの準備
まずSlack API (Apps) でアプリを作成し、以下の設定を行います。
ユーザートークンのスコープに以下を追加
-
channels:history(メッセージ読み取り) -
users:read(表示名取得用)
「Install App」ボタンをおしてワークスペースにインストールし、発行された User OAuth Token (xoxp-...) を控えておきましょう。
2. 全件取得用スクリプト
次にGASを準備していきましょう。
- Google Drive を開き、適当なフォルダ(ログ保存用)を作成します。
- 左上の「+新規」>「その他」>「Google Apps Script」を選択します。
- プロジェクトが開いたら、左上の「無題のプロジェクト」をクリックして「Slack2Text」などの名前を付けて保存します。
- 元からある function myFunction() {...} は消去して、以下のコードを貼り付けていきます。
まずは既存のメッセージをすべてエクスポートするための関数です。
-
SLACK_TOKENは先程のUser OAuth Tokenです -
CHHANNEL_IDSはデータ収集するチャネルのIDのリストです。Slackアプリ上で、エクスポートしたいチャンネル名を右クリックし、「リンクをコピー」し、末尾のC0XXXXXXXのような文字列をカンマ区切りで書いていきます -
FOLDER_IDはGoogle Drive上の保存用フォルダのURLの末尾(folders/◯◯◯ の◯の部分)です
const SLACK_TOKEN = 'YOUR_USER_TOKEN';
const CHANNEL_IDS = ['CXXXXXXXXXX', 'CXXXXXXXXXX']; // 対象チャンネルID
const FOLDER_ID = 'YOUR_DRIVE_FOLDER_ID'; // 保存先のGoogle DriveフォルダID
function exportAllPastMessages() {
const folder = DriveApp.getFolderById(FOLDER_ID);
CHANNEL_IDS.forEach(channelId => {
let allContent = "";
let nextCursor = "";
let pageCount = 1;
// メッセージがなくなるまでループ
do {
let url = `https://slack.com/api/conversations.history?channel=${channelId}&limit=1000`;
if (nextCursor) {
url += `&cursor=${nextCursor}`;
}
const options = {
"method": "get",
"headers": { "Authorization": "Bearer " + SLACK_TOKEN }
};
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
if (data.ok) {
const messages = data.messages; // 履歴取得時は新しい順(降順)で返ってきます
messages.forEach(msg => {
if (!msg.subtype) {
const date = Utilities.formatDate(new Date(msg.ts * 1000), "JST", "yyyy-MM-dd HH:mm");
const userName = getUserName(msg.user); // 前述の名前変換関数を併用
allContent = `[${date}] ${userName}: ${msg.text}\n` + allContent; // 古い順に並ぶよう上に追加
}
});
nextCursor = data.response_metadata ? data.response_metadata.next_cursor : "";
Logger.log(`${channelId}: ${pageCount}ページ目取得完了...`);
pageCount++;
// API負荷軽減のため少し待機
Utilities.sleep(1000);
} else {
Logger.log("エラー: " + data.error);
break;
}
} while (nextCursor);
// ファイルに保存
const fileName = `FULL_HISTORY_${channelId}.txt`;
folder.createFile(fileName, allContent);
Logger.log(`${channelId} の全履歴エクスポートが完了しました。`);
});
}
これを動かすとSlackの投稿が全件テキストファイルで Google Driveに保存されます!
3. 定期取得(差分更新)用スクリプト
次にGASのトリガーで「1時間おき」に実行することを想定したコードです。その日の日付ファイルに最新の書き込みを追記します。
/ ユーザー名のキャッシュ(何度もAPIを叩かないように一時保存)
let userCache = {};
function exportSlackToTextFiles() {
const folder = DriveApp.getFolderById(FOLDER_ID);
const now = new Date();
const today = Utilities.formatDate(now, "JST", "yyyy-MM-dd");
// 現在から1時間前(3600秒前)のタイムスタンプを計算
const oneHourAgo = Math.floor((now.getTime() - (60 * 60 * 1000)) / 1000);
CHANNEL_IDS.forEach(channelId => {
// oldestパラメータを追加して、過去1時間分のみ取得
const url = `https://slack.com/api/conversations.history?channel=${channelId}&oldest=${oneHourAgo}&limit=100`; const options = {
"method": "get",
"headers": { "Authorization": "Bearer " + SLACK_TOKEN }
};
try {
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
if (data.ok) {
let logContent = "";
const messages = data.messages.reverse();
messages.forEach(msg => {
if (!msg.subtype) {
const date = Utilities.formatDate(new Date(msg.ts * 1000), "JST", "HH:mm");
const userName = getUserName(msg.user); // 名前変換関数を呼び出し
logContent += `[${date}] ${userName}: ${msg.text}\n`;
}
});
// フォルダ内にファイルを作成(既存なら追記、新規なら作成)
const fileName = `${channelId}_${today}.txt`;
const files = folder.getFilesByName(fileName);
if (files.hasNext()) {
const file = files.next();
const oldContent = file.getBlob().getDataAsString();
file.setContent(oldContent + logContent);
} else {
folder.createFile(fileName, logContent);
}
Logger.log(`${channelId} のテキスト保存完了`);
}
} catch (e) {
Logger.log(`エラー: ${e.message}`);
}
});
}
// ユーザーIDを名前に変換する関数
function getUserName(userId) {
if (userCache[userId]) return userCache[userId];
const url = `https://slack.com/api/users.info?user=${userId}`;
const options = {
"method": "get",
"headers": { "Authorization": "Bearer " + SLACK_TOKEN }
};
try {
const response = UrlFetchApp.fetch(url, options);
const data = JSON.parse(response.getContentText());
if (data.ok) {
const name = data.user.real_name || data.user.name;
userCache[userId] = name; // キャッシュに保存
return name;
}
} catch (e) {
return userId; // エラー時はIDをそのまま返す
}
return userId;
}
GASを保存したら1時間ごとに実行するように設定しましょう。
4. Geminiで利用
ここまでできたらあとは、ファイルを保管したGoogle Drive上のGeminiや、GeminiのプロンプトでDriveのURLを指定してあげればSlackの情報を活用した検索・文書生成ができます。
私は以下のようなのを試して便利さに感動しました。
- 「最近の各部門の状況を報告して」
- 「〇〇のインシデントの報告書を作成して」
ちなみにテキストになってるので他のAIでも使えますね。AIたのしすぎますね。では!

