Slackは、チームによる効率的なコラボレーションを支える便利なメッセージングプラットフォームです。このコラボレーションがKinstaの複数のWordPressサイトの管理に関係したものであれば、Kinsta APIを活用して、一般的なサイト管理タスクをSlackから実行することも可能です。
SlackBotをワークフローに統合することで、手間をおさえ生産性を上げることができます。この記事では、SlackとKinsta APIを組み合わせて、サイトステータスのチェック、サイトのキャッシュのクリア、サイトPHPエンジンの再起動といったタスクを管理するSlackBotを構築したいと思います。
構築するもの
Bolt API(簡単にSlackプラットフォームでプログラミングを実装できる)を使ってNode.js上でSlackBotを構築します。最終的には、SlackのスラッシュコマンドをKinsta APIの呼び出しに変換し、すべてのユーザーインタラクションがSlackで発生するように応答をリレーすることになります。
前提条件
このプロジェクトについて行くには、以下が必要です。
- JavaScriptとNode.jsの基本的な知識
- Node.jsのバージョン12以上
- npm(Node Package Manager)がコンピュータにインストールされていること
- Slackワークスペース
Slackアプリケーションの作成
SlackBotは基本的にSlackアプリケーションです。Slackアプリケーションを作成するには、以下の手順に従ってください。
- Slack APIダッシュボードに移動
- 「Create New App」ボタンをクリック(ポップアップが開く)
- アプリを一から作るには「From Scratch」を選択
- Slackアプリの名前を入力(例えば、Kinsta Botなど)
- 次に、アプリをインストールするワークスペースを選択し、「Create App」ボタンをクリック
サイドバーの「Basic information」に移動して、アプリの基本情報を編集してください。「Display information」までスクロールして、画像、名前、説明、色などを設定することができます。
oAuthとパーミッションの設定
これでSlackBotの作成は完了です。ただし、Slackワークスペース内でデータにアクセスしたり、アクションを実行したりするには、特定の権限を付与する必要があります。その方法は以下の通りです。
- Slackアプリダッシュボードの左サイドバーにある「OAuth and Permissions」に移動
- そこで、「Scopes」セクションまでスクロール
- ボットに、ユーザーからのインスタントメッセージを読みそのメッセージに返信する権限を与える
以下は、ボットに提供するスコープを示したスクリーンショットです。
これらのスコープを設定することで、ボットはSlackユーザーとシームレスに対話し、意図した機能を実行できるようになります。
ワークスペースへのSlackBotのインストール
続いて、SlackBotをSlackワークスペースにインストールします。左のサイドバーに移動し、「Install Apps」を選択します。そこから、「Install to Workspace」をクリックし、ボットを追加するチャンネルを指定します。
これでSlackBotの準備は完了です。リクエストの受信と処理に使用するNode.jsサーバーをセットアップしましょう。
開発環境のセットアップ
新しいNode.jsプロジェクトを始めるには、アプリケーション用の新しいディレクトリを作成し、npmで初期化します。
mkdir my-express-app
cd my-express-app
npm init -y
上記のコマンドを実行すると、選択したディレクトリにpackage.jsonファイルがデフォルト値で作成されます。package.jsonファイルには、プロジェクトとその依存関係に関する情報が含まれるため非常に重要です。この依存関係は、スムーズな開発を可能にしプロジェクトの機能性を引き上げます。
@slack/bolt
:このJavaScriptフレームワークは、最新のプラットフォーム機能を活用し、機能豊富なSlackアプリを迅速に作成できるようにします。nodemon
:ディレクトリ内のファイルの変更が検出されるたびに、Node.jsアプリケーションを自動で再起動する機能です。。dotenv
:このゼロ依存モジュールは、.envファイルからprocess.env
に環境変数をロードする重要な役割を果たし、設定管理を簡単にします。
必要なパッケージをインストールするには、以下のコマンドを実行します。
npm i @slack/bolt
npm i --save-dev nodemon dotenv
依存関係のインストールが完了したら、今度はpackage.jsonファイルにdevスクリプトを追加して、nodemon
でJavaScriptファイルを実行します。package.jsonスクリプトオブジェクトに以下が含まれていることを確認してください。
"scripts": {
"dev": "nodemon app.js"
}
dev
スクリプトが、存在しないapp.jsファイルを指しているはずです。このファイルは、プロジェクトのロジックを処理するのに使用するので、プロジェクトのディレクトリに作成してください。
touch app.js
環境変数(トークンとシークレット)の設定
Node.jsプロジェクトからSlackアプリケーションとやりとりするには、特定のトークンとシークレットが必要です。この認証情報は機密性が高いものですので、バージョン管理の際に保護するために、.envファイルに格納します。
ステージングシークレットを取得するには、Slackのダッシュボードで「Basic Information」をクリックし、「App Credentials」までスクロールします。「Install App」または「OAuth & Permissions」をクリックすると、OAuth Tokenが見つかります。通常、トークンは「xoxb」で始まります。
プロジェクトのルートディレクトリに.envという名前のファイルを作成し、Signing SecretとBot Tokenを以下のフォーマットで追加します。
SLACK_SIGNING_SECRET="YOUR SIGNING SECRET"
SLACK_BOT_TOKEN="YOUR BOT TOKEN"
この機密情報がGitサービスにプッシュされないようにするには、プロジェクトのルートディレクトリに.gitignoreファイルを作成し、.envファイルとnode_modulesフォルダを無視するように以下の行を追加します。
/node_modules
.env
この設定が完了したら、次はサーバーの設定です。
Node.jsサーバーのセットアップ
Node.jsサーバーのセットアップは、SlackBotを構築する上で重要なステップです。必要なパッケージとモジュールをインポートして初期化し、プロジェクトがリッスンするポートを設定する必要があります。先ほど作成したapp.jsファイルを開き、以下のコードを追加します。
const { App } = require("@slack/bolt");
require("dotenv").config();
// ボットトークンとシークレットでアプリを初期化
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
});
(async () => {
// Start your app
await app.start(process.env.PORT || 3000);
console.log(`⚡️ Kinsta Bot app is running on port ${process.env.PORT || 3000}!`);
})();
上のコードでは、@slack/bolt
ライブラリからApp
クラスをインポートしています。これは、Slack アプリを構築するための重要なコンポーネントです。さらに、dotenv
パッケージを使用して環境変数を処理しています。
次に、app
定数をApp
クラスを使用して作成(Slackボットアプリケーションを表す)します。これには2つの重要なパラメータtoken
とsigningSecret
が必要です。これらの値は.envファイルから取得されます。
次に、async
関数の中で、サーバーのセットアップが行われます。アプリはapp.start()
を呼び出すことで開始します。ローカルでポート3000
をリッスンし、KinstaBotアプリが実行されていることを確認するメッセージをコンソールに出力します。
これで、設定したdev
スクリプトを実行すると(npm run dev
)、ターミナルに「⚡️ Kinsta Bot app is running on port 3000!」というメッセージが表示されます。
Slack APIソケットモードを理解する
Node.jsサーバーとSlack APIの統合では、主に2つの接続方法があります─サーバーのパブリックHTTPエンドポイントを利用するか、WebSocketを有効にするSocketモードかです。今回の説明では、Slack のスラッシュコマンドのようなインタラクティブな機能をより簡単に実装できるSocket Modeの利用に焦点を当てます。このアプローチにより、Slackは従来のHTTPの代わりにWebSocketを使用してNode.jsサーバーに接続することができます。
ただし、ローカルでHTTPエンドポイントを使用する場合は、ngrokを活用してローカルホストにプロキシするパブリックURLを作成可能です。
ソケットモードを始める
ソケットモードを使い始めるには、以下の手順に従ってください。
- Slackダッシュボードの左サイドバーにある「Basic Information 」に移動します。「App-Level Tokens」までスクロールし、「Generate Token and Scopes」ボタンをクリックします。
- トークンに名前を付け、
connections:write
とauthorizations:read
の2つのスコープを追加します。そして「Generate」をクリックしてトークンを生成します。 - 生成したトークンをコピーし、.envファイルに貼り付けて、
APP_TOKEN
という変数に代入します。アプリレベルのトークン文字列はxapp-
で始まることをお忘れなく。
次に、サイドバーの「Socket Mode」をクリックし、「Enable Socket Mode」を有効にします。最後に、app.jsファイルで、socketMode:true
とappToken
を、アプリ/サーバーを初期化するコードに追加します。
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
socketMode: true, // enable socket mode
appToken: process.env.APP_TOKEN,
});
これらの手順を完了すると、開発サーバーへのリクエストはすべてHTTPではなくWebSocket経由で行われるようになります。この設定により、Socket Modeを最大限に活用し、SlackBotの機能を強化することができます。
スラッシュコマンド
スラッシュコマンドはSlackの強力な機能で、特定のアクションのカスタムトリガーを可能にします。このトリガーは、Slack内のチャンネル間のチャットメッセージで検出することができます。さらに、スラッシュコマンドを使うと、テキストデータを直接サーバーに渡すことができます。例えば、/operation_status [operation ID]
のようなコマンドを設定すると、指定されたoperation ID
をサーバーに渡し、対応するoperation_status
コマンドリスナーをトリガー可能です。
サーバーがSlackとやり取りできるように適切に設定されたら、次はサーバー上でアクションをトリガーするスラッシュコマンドを設定します。
Slackでスラッシュコマンドを作成する
Slackでスラッシュコマンドを作成するには、左サイドバーの「Slash Commands」をクリックし、「Create New Command」ボタンをクリックします。下の画像を参考に、フォームに必要事項を入力してください。
フォームに情報を入力したら、「Save」ボタンをクリックします。すると、変更を有効にするためにワークスペースにアプリを再インストールするよう促されます。以下の画像のように、指示に従ってスラッシュコマンドをいくつか作成してください。
Node.jsでスラッシュコマンドを設定する
必要なスラッシュコマンドを作成したら、それに応答するようにNode.jsアプリを調整します。
まずは、/operation_status
コマンドをテストしてみましょう。以下のコードをapp.jsファイルに追加して、/operation_status
コマンドを含むイベントのリスナーを設定します。
const { App } = require('@slack/bolt');
require('dotenv').config();
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
socketMode: true, // enable the following to use socket mode
appToken: process.env.APP_TOKEN,
});
app.command('/operation_status', async ({ command, ack, say }) => {
await ack();
say('Wooah! Iit works!');
});
(async () => {
// Start your app
await app.start(process.env.PORT || 3000);
console.log(`⚡️ Kinsta Bot app is running on port ${process.env.PORT || 3000}!`);
})();
上のコードでは、app.command()
関数に注目しています。この関数は、JavaScript のイベントリスナーと同じように機能します。リッスンしたいコマンドを指定し、非同期コールバック関数を作成して目的のアクションを定義します。この関数は3つのパラメータを受け取ります。
command
:ユーザーによって送信されたスラッシュコマンドの情報ack
:スラッシュコマンドの受信を確認するsay
:Slackチャンネルにメッセージを送り返す
上記のコードで、Slackの/operation_status
コマンドが「”Wooah!” it works!」というメッセージを生成します。
それでは、作成したすべてのスラッシュコマンドのコマンドリスナーを追加しましょう。
app.command('/environment_id', async ({ command, ack, say }) => {
await ack();
// コマンドを使用して必要なアクションを実行し、応答を送信
});
app.command('/site_id', async ({ command, ack, say }) => {
await ack();
// コマンドを使用して必要なアクションを実行し、応答を送信
});
app.command('/operation_status', async ({ command, ack, say }) => {
await ack();
// コマンドを使用して必要なアクションを実行し、応答を送信
});
app.command('/clear_site_cache', async ({ command, ack, say }) => {
await ack();
// コマンドを使用して必要なアクションを実行し、応答を送信
});
app.command('/restart_php_engine', async ({ command, ack, say }) => {
await ack();
// コマンドを使用して必要なアクションを実行し、応答を送信
});
これでアプリがSlackのスラッシュコマンドをリッスンする準備が整いました。各コマンドがトリガーするアクションを追加しましょう。
Kinsta APIでスラッシュコマンドを実装する
アプリがKinsta APIへの呼び出しを用い各スラッシュコマンドに応答し、そのアクションの結果をSlackに返すことになります。Kinsta APIを使用するには、MyKinstaに少なくとも1つのWordPressサイト、アプリケーション、またはデータベースのアカウントが必要です。また、APIを通してアカウントを認証し、アクセスするためにAPIキーを生成する必要があります。
Kinsta APIキーの作成方法
APIキーを生成する方法は以下の通りです。
- MyKinstaに移動
- 「APIキー」ページに移動((あなたのアカウント名)>「企業の設定」>「APIキー」)
- 「APIキーを作成」をクリックします。
- 有効期限を選択するか「カスタム」からキーの有効期限を指定
- キーに一意の名前を付ける
- 「生成」をクリック
APIキーを作成したら、それをコピーして安全な場所に保管してください(後から表示することはできませんのでご注意ください)。このプロジェクトの場合は、.envファイルにKINSTA_API_KEY
という名前で保存します。
Node.jsでKinsta APIと対話する
Kinsta APIとの対話は、Axiosを含む多くのNode.jsライブラリを使用して実現できます。しかし、この説明では、最新のNode.jsバージョンでサポートされ、効率的に実行できるJavaScriptのfetch()
メソッドを選択します。
このSlackBotでは、GETリクエストやPOSTリクエストなど、多くのAPIリクエストが行われます。繰り返しを避けるために、API URLとヘッダーを変数に保存して、コードの保守と読み取りを簡単にしましょう。
// kinsta APIユーティリティ
const KinstaAPIUrl = 'https://api.kinsta.com/v2';
const getHeaders = {
Authorization: `Bearer ${process.env.KINSTA_API_KEY}`,
};
const postHeaders = {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.KINSTA_API_KEY}`,
};
各スラッシュコマンドに対するアプリのレスポンスをコーディングし始める前に、Kinstaの企業IDをコピーし、.envファイルにKINSTA_COMPANY_ID
として保存します。これはサイト一覧を取得するために必要になります。
環境IDスラッシュコマンドの実装
/environment_id
スラッシュコマンドを使用すると、コマンドの後に指定された値が検索され、Node.jsサーバーで使用されます。このコマンドには、パラメータが期待されることを示すヒントが付帯します([Site name]
)。
MyKinstaの各サイトには固有のサイト名がありますが、その名前を使用してサイトの環境IDを要求する直接的なエンドポイントはありません。そのため、まず企業アカウントのすべてのサイトに対してリクエストを行い、find()
メソッドを使用して、slashコマンドで渡された名前と一致するサイトを検索します。
そのために、2つのリクエストを行います。まず、サイトのIDを取得し、次に/environments
エンドポイントに別のリクエストを行い、そのサイトに関連付けられている環境IDを取得します。
コードの明快さとメンテナンスのしやすさを維持するために、各リクエストは独立して行います。つまり、リクエストのために個別の関数を持ち、コマンドリスナーでその関数を呼び出すことになります。
すべてのサイトの一覧を取得することから始めましょう。
async function getAllSites() {
const query = new URLSearchParams({
company: process.env.KINSTA_COMPANY_ID,
}).toString();
const resp = await fetch(`${KinstaAPIUrl}/sites?${query}`, {
method: 'GET',
headers: getHeaders,
});
const data = await resp.json();
return data;
}
上記のコードにより、すべてのサイトを含む配列が返されます。/environment_id
コマンドリスナーで、レスポンスを取得し、変数に格納します。次に、find()
メソッドを使用して、Slackから渡された名前と一致するサイトを検索します。Slackからの情報はcommand.text
に格納されます。
app.command('/environment_id', async ({ command, ack, say }) => {
await ack();
let siteName = command.text;
let response = await getAllSites();
if (response) {
let mySites = response.company.sites;
let currentSite = mySites.find((site) => site.name === siteName);
// get environment ID
}
});
サイトが見つかったら、そのIDを使って環境IDを取得します。サイトの一覧を問い合わせたときと同様に、/environments
エンドポイントにHTTPリクエストを行う専用の関数を作成します。
async function getEnvironmentId(siteId) {
const resp = await fetch(`${KinstaAPIUrl}/sites/${siteId}/environments`, {
method: 'GET',
headers: getHeaders,
});
const data = await resp.json();
return data;
}
上記のコードでは、/environment_id
コマンドリスナーでこの関数を呼び出す際に、引数としてサイトIDが渡されることを想定しています。IDを受け取ると、APIリクエストが行われ、レスポンスが変数に格納されます。その後、say()
メソッドを使って環境 IDをSlackに出力できます。
app.command('/environment_id', async ({ command, ack, say }) => {
await ack();
let siteName = command.text;
let response = await getAllSites();
if (response) {
let mySites = response.company.sites;
let currentSite = mySites.find((site) => site.name === siteName);
let envIdResponse = await getEnvironmentId(currentSite.id);
let envId = envIdResponse.site.environments[0].id;
if (envId) {
say(`Hey 👋,\n\nThe environment ID for "${siteName}" is 👉 ${envId}`);
}
}
});
次にSlackを開き、/environment_id
の後に有効なサイト名(/environment_id fashionstored
など)を入力すると、次のようなレスポンスが返ってきます。
サイトツール(サイトキャッシュのクリア、PHPエンジンの再起動)スラッシュコマンドの実装
MyKinstaに移動することなく、APIを通じて簡単に実装できる2つのタスクとして、サイトツールの操作である「サイトキャッシュのクリア」と「PHPエンジンの再起動」を選択します。
サイトツールの操作を実行するには、環境IDが必要になります。このIDを使用して、/sites/tools/clear-cache
と/sites/tools/restart-php
にそれぞれPOSTリクエストを行います。先ほどと同様に、APIリクエストを単独で実行し、レスポンスを返します。
async function clearSiteCache(environmentId) {
const resp = await fetch(`${KinstaAPIUrl}/sites/tools/clear-cache`, {
method: 'POST',
headers: postHeaders,
body: JSON.stringify({
environment_id: environmentId,
}),
});
const data = await resp.json();
return data;
}
async function restartPHPEngine(environmentId) {
const resp = await fetch(`${KinstaAPIUrl}/sites/tools/restart-php`, {
method: 'POST',
headers: postHeaders,
body: JSON.stringify({
environment_id: environmentId,
}),
});
const data = await resp.json();
return data;
}
次に、Slackに両方の操作のコマンドリスナーを作成します。これらのリスナーは、それぞれのコマンドが使われるたびにトリガーされるように設定します。
app.command('/clear_site_cache', async ({ command, ack, say }) => {
await ack();
let environmentId = command.text;
let response = await clearSiteCache(environmentId);
if (response) {
say(
`Hey 👋, \n\n${response.message} by using the /operation_status slack commmand. \n\nOperation Id is ${response.operation_id}`
);
}
});
app.command('/restart_php_engine', async ({ command, ack, say }) => {
await ack();
let environmentId = command.text;
let response = await restartPHPEngine(environmentId);
if (response) {
say(
`Hey 👋, \n\n${response.message} by using the /operation_status slack command. \n\nOperation Id is ${response.operation_id}`
);
}
});
上のコードでは、APIリクエストから取得したデータを使って、Slackに送り返すレスポンスを構成しています。レスポンスには、メッセージや操作IDなどの操作に関する情報が含まれます。
このようにスラッシュコマンドと対応するリスナーを実装することで、SlackBotがKinsta APIとシームレスにやり取りできるようになり、サイトのキャッシュやPHPエンジンをSlackから直接管理できます。
操作ステータススラッシュコマンドの実装
実行状況をSlackから取得するのも有用です。これには、operation_id
と並んで/operations
エンドポイントを使います。先ほどと同じように、このリクエストを処理してリクエストのレスポンスを返す関数を作成します。
async function CheckOperationStatus(operationId) {
const resp = await fetch(`${KinstaAPIUrl}/operations/${operationId}`, {
method: 'GET',
headers: getHeaders,
});
const data = await resp.json();
return data;
}
リクエストをトリガーするコマンドリスナーを定義し、Slack経由で送信されたoperation ID
をわたすように設定しましょう。
app.command('/operation_status', async ({ command, ack, say }) => {
await ack();
let operationId = command.text;
let response = await CheckOperationStatus(operationId);
let operationMessage = response.message;
if (operationMessage) {
say(`Hey 👋, \n\n${operationMessage}`);
}
});
これで、/operation_status
のスラッシュコマンドを有効なoperation ID
で使用すると、Slack経由でIDのステータスが表示されるようになります。
Kinsta APIですでに実装したコマンドに加えて、さらに統合できるコマンドや、SlackBotが処理できるイベントがあります。例えば、@
を使って言及されたり、タグ付けされたりすると、SlackBotが反応することができます。
この機能を有効にするには、Slackで必要なイベントに反応する仕組みが必要になります。app.command()
メソッドを使う代わりに、command
とsay
の両方のパラメータを受け取るapp.message()
メソッドを使うことができます。
以下がその例です。
app.message("hey", async ({ command, say }) => {
say("Woah! It works!");
});
SlackBotの作成後に、メッセージを送信できないことに気づくかもしれません。Slackのアプリタブからボットに移動すると、次のようなメッセージが(英語で)表示されます─「このアプリへのメッセージ送信はオフになっています」─しかし、心配はいりません。
メッセージ送信を有効にするには、以下の手順に従ってください。
- 左サイドバーの「App Home」メニューをクリックします。
- このページでボットのすべての設定を管理できます。下にスクロールして、「Allow users to send Slash commands and messages from the messages tab」のチェックボックスを見つけます。
- これにチェックを入れると、機能が有効になります。
この変更を行ったら、Slackアプリを再読み込みしてアップデートを反映させます。Macであれば、CMD + RでSlackを再読み込みできます。
これで、ボットにメッセージを送る準備は完了です。Slackアプリの「Your Apps」セクションのすぐ下にKinsta Botアプリが表示されるはずです。それをクリックしてメッセージの送信を開始します。設定したスラッシュコマンドを自由にテストし、SlackBotとのシームレスなやりとりをお楽しみください。
このプロジェクトのソースコード一式はGitHubで公開しています。
Node.jsアプリケーションをKinstaにデプロイする
Node.jsサーバーをビルドしたら、デプロイに進みましょう。コードがGitサービスのBitbucket、GitHub、またはGitLabでホストされている場合、Kinstaのアプリケーションホスティングプラットフォームにそのままデプロイすることができます。
リポジトリをKinstaにデプロイするには、以下の手順に従ってください。
- MyKinstaでKinstaアカウントにログイン
- 「サービスを追加」をクリック
- ドロップダウンメニューから「アプリケーション」を選択
- 表示されるポップアップで、デプロイしたいリポジトリを選択(複数のブランチがある場合は、希望のブランチを選択 ※アプリケーションに名前をつけることも可)
- データセンターの場所を1つ選択(自動でpackage.jsonからアプリ依存関係の検出、インストール、そしてビルドとデプロイが行われます)
最後に、GitプロバイダのようなパブリックホストにAPIキーをプッシュアウトするのは安全ではありません。Kinstaでホスティングする場合、開発用の.envファイルで指定された同じ変数名と値を使用して、環境変数として追加することができます。
アプリケーションのデプロイを行うと、プロセスが始まり、通常は数分以内に完了します。Node.jsの設定に問題がある場合、デプロイ設定タブでNode.jsビルドパックを追加できます。
まとめ
この記事では、SlackとKinsta APIを接続するNode.jsアプリケーションの構築方法と、そのアプリケーションをKinstaにデプロイする方法をご紹介しました。
SlackBotとKinsta APIを使うことで、Kinstaでホストするサービスの監視と管理が容易になります。この基本を踏まえて、SlackBotとAPIを使って何ができるか想像してみてください。SlackからWordPressサイトの複製やサイトの作成のような操作を実行する、より複雑なコマンドも考えられます。
あなたはKinsta APIをどのように使っていますか?次にどのような機能を追加/公開してほしいですか?
コメントを残す