
Slackなどのビジネスチャットツールを日常業務に活用している企業も多いと思います。
気軽に情報共有できる便利な反面、「感情の見えにくさ」や「誤爆のリスク」に悩まされるケースも少なくありません。
たとえば
-
お客様や取引先からの不満がSlack上に投稿されていたが、上司が気づくのが遅れた
-
社内のメンバーが、静かな怒りやストレスを投稿していたが、気づかないまま放置されていた
-
社内の愚痴やネガティブな発言を、うっかり取引先チャットに誤爆してしまった
実際、私自身も取引先の部下がSlack上で上層部への不満を投稿していたにも関わらず、上層部がしばらく気が付いていなかったという場面に立ち会ったことがあります。
投稿は全社に筒抜けの状態だったにも関わらず、誰も指摘せずスルーされていたことに驚くと同時に、「Slack上の空気」は意外と見落とされやすいものなのだと痛感しました。このように、Slackは便利である一方で、こうしたリスク管理の穴になりがちな側面もあるのです。
ChatGPTとGASで、Slackの“空気”を見える化!
そこで今回は、ChatGPT APIとGoogle Apps Script(GAS)を活用して、Slack上の発言を感情分析し、ネガティブ度が高ければメールで通知する仕組みを構築してみました。
実際のログ分析結果を見てみましょう。
発言例①(丁寧だけど心の中は…)

▼メールでの通知内容

このように、文面は丁寧でも「退職も考えています」というキーワードがAIによって適切に評価され、上司に通知されます。
発言例②(ストレートな怒り)
ユーザー:うざ。ふざけんなよ。

こちらはストレートな怒りの発露。このレベルの内容であれば誤爆の可能性の方が高そうですが、ネガティブ度:9、不適切・攻撃性あり と分析され、即時通知が送信されました。
発言例③(会話の流れの中で怒りを検知)

1つ前の「再考いただければ~」までは未通知、見積もりの内容について不満や不安があることは伝わっているものの、この段階ではネガティブ度は6となり、ギリギリ未通知。
追随の「おかしいですよね?」の内容について通知がきています。

取引先が静かに怒っていることをすぐに察知でき、対策を講じることができます。
導入方法は?
Slackから取得した発言をGASで読み込み、ChatGPT APIに感情分析を依頼、
ネガティブ度が高ければメールで通知、というシンプルな流れです。
🔧 必要な準備
-
Slack API でAppを作成




-
サイドバーのOAuth & Permissions から以下のスコープを設定:
channels:history(パブリックチャンネル)
groups:history(プライベートチャンネル)
im:history(DMの履歴)
channels:read(チャンネル情報)
users:read(ユーザー名取得)

-
Botを作成


-
Bot Tokenを取得

-
ChatGPT API Keyを取得
keiridx.net
-
Google Apps Script(GAS)にコードを記述 ※当ブログの一番下に記載しています。
-
トリガーで定期実行!
keiridx.net
実際どう使う?
人事部・総務部で従業員のメンタルヘルスケアの一環として導入したり、
プロジェクト単位で「Slackの空気が荒れていないか」を可視化するダッシュボードと連携したり、様々な場面で活用可能です。
「何も起こらない」が一番ですが、“もしも”に気づける体制があるだけで、組織の安心感はグッと高まります。
まとめ:Slackの“感情”にもセンサーを
Slackは、便利さとリスクが背中合わせのツールです。
ChatGPTとGASを活用すれば、そうした見えにくい“感情の揺らぎ”を可視化し、
早期対応につなげることが可能になります。
「AIによるネガティブ検知」は、組織の安全管理にもつながる第一歩。
ぜひ一度、実験的にでも導入してみてはいかがでしょうか。
【関連リンク】
▼人事評価AIで新入社員の離職率を下げる方法
keiridx.net
▼GoogleドライブとSlack連携でファイル通知を自動化
keiridx.net
▼GASとChatGPTの連携でさらなるカスタマイズをしたい方はこちらの書籍もおすすめです。
▼ 役に立ったらブックマークお願いします!


人気ブログランキング
<GAS Code>
const SLACK_TOKEN = "****************************"; // ← Slack botトークン
const CHANNEL_ID = "*************"; // ← チャンネルID
const OPENAI_API_KEY = "**************"; // ← chatGPT API
const RECIPIENT_EMAIL = "(ここにあなたのメールアドレス)";
function getSlackMessagesWithReplies() {
const now = new Date();
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const oldest = Math.floor(yesterday.getTime() / 1000);
const latest = Math.floor(now.getTime() / 1000);
const options = {
method: "get",
headers: {
Authorization: `Bearer ${SLACK_TOKEN}`,
},
muteHttpExceptions: true,
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
Logger.log("Slack APIエラー: " + json.error);
return ;
}
const messagesWithReplies = ;
json.messages.forEach(msg => {
messagesWithReplies.push(msg);
if (msg.thread_ts && msg.thread_ts === msg.ts) {
const replies = getThreadReplies(msg.thread_ts);
messagesWithReplies.push(...replies);
}
});
return messagesWithReplies;
}
function getThreadReplies(threadTs) {
const options = {
method: "get",
headers: {
Authorization: `Bearer ${SLACK_TOKEN}`
}
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
return json.messages.slice(1); // 親メッセージを除外
} else {
Logger.log("返信の取得に失敗: " + json.error);
return [];
}
}
function getUserName(userId) {
const options = {
method: "get",
headers: {
Authorization: `Bearer ${SLACK_TOKEN}`
}
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
Logger.log(`ユーザーID: ${userId}`);
Logger.log(`ユーザー情報: ${JSON.stringify(json)}`);
return json.user.real_name || json.user.name;
} else {
return userId;
}
}
function analyzeSlackMessages() {
const messages = getSlackMessagesWithReplies();
if (!messages || messages.length === 0) {
Logger.log("投稿がありません");
return;
}
MailApp.sendEmail(RECIPIENT_EMAIL, "⚠ ネガティブ投稿を検知しました", ...);
`⚠ ネガティブ投稿を検知しました!\n\n投稿者:${userName}\n時刻 :${ts}\n内容 :「${text}」\n\n分析結果:\n${result}`);
}
});
}
function callChatGPT(prompt) {
const payload = {
model: "gpt-4o",
messages: [
{ role: "system", content: "あなたはビジネスチャットの感情分析を行う専門AIです。" },
{ role: "user", content: prompt }
],
temperature: 0.4,
max_tokens: 300
};
const options = {
method: "post",
headers: {
Authorization: `Bearer ${OPENAI_API_KEY}`,
"Content-Type": "application/json"
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
const response = UrlFetchApp.fetch(url, options);
const json = JSON.parse(response.getContentText());
if (json.choices && json.choices.length > 0) {
return json.choices[0].message.content.trim();
} else {
return "分析できませんでした。";
}
}
const text = msg.text;
const userId = msg.user || "BOT";
const userName = getUserName(userId);
const ts = new Date(parseFloat(msg.ts) * 1000).toLocaleString("ja-JP");
const prompt = `以下のSlack発言に含まれる感情を分析してください。\n\n発言:「${text}」\n\n1. ネガティブ度(10段階)\n2. 怒り・ストレス・苛立ちの兆候(ある・ない)\n3. 言葉づかいに不適切・失礼・攻撃性があるか(ある・ない)\n4. 投稿者の感情状態について一言アドバイス`;
const result = callChatGPT(prompt);
Logger.log(`【${index + 1}】${userName}(${ts})\n${text}\n>>> 分析結果:\n${result}\n---`);
if (result.includes("ネガティブ度(10段階):7") ||
result.includes("ネガティブ度(10段階):8") ||
result.includes("ネガティブ度(10段階):9") ||
result.includes("ネガティブ度(10段階):10"