Slack
コミュニケーションツール
Slack
概要
Slackは、チャンネルベースのチームコミュニケーションプラットフォームです。2,000以上のアプリ統合をサポートし、ワークフローの自動化とBot機能が充実しています。開発者フレンドリーなAPIにより、カスタム統合や自動化が容易に実現できます。
詳細
Slack(スラック)は、2013年にリリースされたビジネス向けコミュニケーションプラットフォームです。現在、1,000万人以上のデイリーアクティブユーザーを抱え、スタートアップから大企業まで幅広く採用されています。2021年にSalesforceによって買収されましたが、独立したサービスとして成長を続けています。
Slackの最大の特徴は、チャンネルベースの組織的なコミュニケーションと、豊富な外部連携機能です。REST APIやWebhook、リアルタイムメッセージング(RTM API)、最新のSocket Modeなど、多様な統合手法を提供しています。2024年には新しいWorkflow Builder機能が強化され、ノーコードでの自動化がより簡単になりました。また、AI機能(Slack AI)やAgentforce Agentsの導入により、インテリジェントな自動化も可能になっています。
Block Kit UIフレームワークにより、リッチなインタラクティブ要素を備えたメッセージやモーダルを作成でき、Bolt フレームワーク(JavaScript、Python)を使用することで、効率的なアプリ開発が可能です。
メリット・デメリット
メリット
- 豊富な統合機能: 2,000以上のアプリ統合をサポート
- 強力なAPI: REST API、WebSocket(Socket Mode)、Webhook等
- 開発者フレンドリー: Bolt フレームワークによる効率的な開発
- Block Kit UI: リッチなインタラクティブ要素を簡単に作成
- ワークフロー自動化: Workflow Builderでノーコード自動化
- AI機能統合: Slack AIとAgentforce Agentsによるインテリジェント化
- セキュリティ: エンタープライズグレードのセキュリティ機能
- スケーラビリティ: 小規模チームから大企業まで対応
デメリット
- 価格: 高機能プランは比較的高価
- 学習コスト: 高度な機能活用には習熟が必要
- API制限: レート制限や利用制限が存在
- ストレージ制限: 無料プランでは履歴とファイル容量に制限
- 通知過多: 適切な設定なしでは通知が多すぎる場合も
- 複雑性: 多機能すぎて小規模チームには過剰な場合も
主要リンク
- Slack公式サイト
- Slack API Documentation
- Bolt for JavaScript
- Bolt for Python
- Block Kit Builder
- Slack App Directory
- Slack Developer Program
書き方の例
Hello World Bot(Bolt.js)
const { App } = require('@slack/bolt');
// Slack Botの初期化
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
// "hello"を含むメッセージに反応
app.message('hello', async ({ message, say }) => {
await say(`Hello <@${message.user}>! 👋`);
});
// アプリの起動
(async () => {
await app.start(process.env.PORT || 3000);
console.log('⚡️ Slack Bot is running!');
})();
Socket Mode設定
const { App } = require('@slack/bolt');
// Socket Modeでの初期化(開発時推奨)
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
appToken: process.env.SLACK_APP_TOKEN,
socketMode: true, // Socket Modeを有効化
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
// WebSocket接続でリアルタイム通信
(async () => {
await app.start();
console.log('⚡️ Bolt app is running in Socket Mode!');
})();
インタラクティブなBlock Kit UI
// ボタン付きメッセージの送信
app.message('menu', async ({ message, say }) => {
await say({
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `Hey <@${message.user}>! What would you like to do?`
},
accessory: {
type: "button",
text: {
type: "plain_text",
text: "Click Me"
},
action_id: "button_click"
}
}
]
});
});
// ボタンクリックの処理
app.action('button_click', async ({ ack, respond, client }) => {
await ack();
await respond({
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: "Button clicked! Here's your menu:"
}
},
{
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "Option 1" },
action_id: "option_1"
},
{
type: "button",
text: { type: "plain_text", text: "Option 2" },
action_id: "option_2"
}
]
}
]
});
});
スラッシュコマンド
// /hello コマンドの実装
app.command('/hello', async ({ command, ack, respond }) => {
await ack();
const user = command.user_name;
const text = command.text;
await respond({
response_type: 'in_channel', // チャンネル内で表示
text: `Hello ${user}! You said: "${text}"`
});
});
// /weather [city] コマンドの実装
app.command('/weather', async ({ command, ack, respond }) => {
await ack();
const city = command.text;
if (!city) {
await respond({
response_type: 'ephemeral', // 実行者のみに表示
text: 'Please specify a city: `/weather Tokyo`'
});
return;
}
// 外部API呼び出し例
try {
const weather = await getWeatherInfo(city);
await respond({
response_type: 'in_channel',
text: `Weather in ${city}: ${weather.description}, ${weather.temperature}°C`
});
} catch (error) {
await respond({
response_type: 'ephemeral',
text: `Sorry, couldn't get weather for ${city}`
});
}
});
イベント処理(チーム参加時の歓迎メッセージ)
const WELCOME_CHANNEL = 'C12345'; // 歓迎チャンネルのID
// 新メンバー参加時の処理
app.event('team_join', async ({ event, client, logger }) => {
try {
const result = await client.chat.postMessage({
channel: WELCOME_CHANNEL,
text: `Welcome to the team, <@${event.user.id}>! 🎉 Please introduce yourself in this channel.`,
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `Welcome to the team, <@${event.user.id}>! 🎉`
}
},
{
type: "section",
text: {
type: "mrkdwn",
text: "Please introduce yourself and don't hesitate to ask questions!"
}
},
{
type: "actions",
elements: [
{
type: "button",
text: { type: "plain_text", text: "View Team Guide" },
action_id: "view_guide"
}
]
}
]
});
logger.info('Welcome message sent:', result);
} catch (error) {
logger.error('Error sending welcome message:', error);
}
});
Webhook統合
// 外部サービスからのWebhook受信
app.post('/webhook/github', async (req, res) => {
const payload = req.body;
if (payload.action === 'opened' && payload.pull_request) {
const pr = payload.pull_request;
// プルリクエスト通知をSlackに送信
await app.client.chat.postMessage({
token: process.env.SLACK_BOT_TOKEN,
channel: '#development',
text: `New Pull Request: ${pr.title}`,
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: `*New Pull Request*\n<${pr.html_url}|${pr.title}>`
}
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text: `Created by ${pr.user.login} in ${payload.repository.full_name}`
}
]
}
]
});
}
res.status(200).send('OK');
});
ワークフロー自動化(Custom Function)
// カスタムワークフロー関数の実装
app.function('create_jira_issue', async ({ inputs, complete, fail }) => {
try {
const { project, summary, description, priority } = inputs;
// Jira APIに新しい課題を作成
const jiraResponse = await fetch(`https://${process.env.JIRA_DOMAIN}/rest/api/latest/issue`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.JIRA_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
fields: {
project: { key: project },
summary: summary,
description: description,
issuetype: { name: 'Task' },
priority: { name: priority }
}
})
});
const issue = await jiraResponse.json();
// ワークフローに結果を返す
await complete({
outputs: {
issue_id: issue.id,
issue_key: issue.key,
issue_url: `https://${process.env.JIRA_DOMAIN}/browse/${issue.key}`
}
});
} catch (error) {
console.error('Error creating Jira issue:', error);
await fail({ error: error.message });
}
});
スケジュールメッセージとリマインダー
// スケジュールメッセージの送信
app.message('remind me', async ({ message, say, client }) => {
const text = message.text;
const match = text.match(/remind me (.+) in (\d+) (minutes?|hours?|days?)/i);
if (!match) {
await say('Format: "remind me [message] in [number] [minutes/hours/days]"');
return;
}
const [, reminderText, amount, unit] = match;
// 時間計算
const multipliers = {
'minute': 60,
'minutes': 60,
'hour': 3600,
'hours': 3600,
'day': 86400,
'days': 86400
};
const seconds = parseInt(amount) * (multipliers[unit] || 60);
const scheduleTime = Math.floor(Date.now() / 1000) + seconds;
try {
const result = await client.chat.scheduleMessage({
channel: message.channel,
post_at: scheduleTime,
text: `🔔 Reminder: ${reminderText}`
});
await say(`✅ Reminder set! I'll remind you in ${amount} ${unit}.`);
} catch (error) {
await say('Sorry, I couldn\'t schedule that reminder.');
}
});
環境変数設定
# .env ファイル
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-token
SLACK_SIGNING_SECRET=your-signing-secret
# Socket Mode用(開発時)
SLACK_SOCKET_MODE=true
# 外部サービス統合用
JIRA_DOMAIN=your-company.atlassian.net
JIRA_TOKEN=your-jira-token
GITHUB_WEBHOOK_SECRET=your-webhook-secret
アプリマニフェスト例
{
"display_information": {
"name": "Development Assistant",
"description": "A helpful bot for development teams",
"background_color": "#2c2d30"
},
"features": {
"bot_user": {
"display_name": "DevBot",
"always_online": true
},
"slash_commands": [
{
"command": "/hello",
"description": "Say hello",
"usage_hint": "[message]"
},
{
"command": "/weather",
"description": "Get weather info",
"usage_hint": "[city]"
}
]
},
"oauth_config": {
"scopes": {
"bot": [
"chat:write",
"commands",
"channels:read",
"groups:read",
"im:read",
"mpim:read"
]
}
},
"settings": {
"event_subscriptions": {
"bot_events": [
"team_join",
"message.channels",
"message.groups",
"message.im",
"message.mpim"
]
},
"interactivity": {
"is_enabled": true
},
"socket_mode_enabled": true
}
}