![[Twilio+Slack] Slack コマンドで音声読み上げ通話を自動発信する: SQS+Lambda での非同期処理構成 [2/3]](https://images.ctfassets.net/ct0aopd36mqt/wp-thumbnail-3f316c20ff5f4f46d1a005e2ad976882/c71162912c717bb0ca7c9b4196a40df5/twilio.png)
[Twilio+Slack] Slack コマンドで音声読み上げ通話を自動発信する: SQS+Lambda での非同期処理構成 [2/3]
- 第 1 回: 構成と Twilio 初期設定
- 第 3 回: Slack アプリの設定と動作確認
対象読者
- Slack スラッシュコマンドから電話発信を行いたい方
- Twilio の音声読み上げ通話機能(TwiML
Say
)を試したい方 - AWS Lambda + SQS + API Gateway の連携例を学びたい方
- Slack の 3 秒タイムアウト制限を回避したい方
- Twilio 利用時の課金リスク回避設計にも関心がある方
参考記事
検証環境
バージョン
node: 22.13.1
npm: 10.9.2
node_modules/twilio: 5.5.1
node_modules/aws-sdk: 2.1692.0
概要
本記事では、Slack のスラッシュコマンド /voice
を使って、Twilio 経由で自動音声通話を発信し、指定した内容を読み上げる仕組みを構築する方法を紹介します。第 2 回では、Slack からのリクエストを受け取り、Twilio を使って音声通話を非同期で発信するためのバックエンド構成を実装します。
具体的には、以下の 3 つのコンポーネントを実装します。
- Amazon SQS:非同期処理のためのメッセージキュー
- Lambda 関数(前段):Slack からのリクエストを受信し、SQS に送信
- Lambda 関数(後段):SQS をトリガーに Twilio API を呼び出し、通話を実行
構成図のうち、赤枠部分の実装を行います。
SQS キューの作成
Amazon SQS コンソール にアクセスし、左メニューの「キュー」を選択、画面右上の「キューを作成」ボタンをクリックします。
以下のように設定します。
項目 | 設定値 |
---|---|
キュータイプ | 標準 |
キュー名 | slack-call-queue(任意) |
他の項目はデフォルト設定のままで構いません。「キューの作成」をクリックして完了します。
キューの URL を控える
作成したキューの詳細画面に表示される「URL」をコピーしておきます。この URL は、Slack からのリクエストを受け取る Lambda 関数からメッセージを送信する際に使用します。
SQS からのメッセージを受け取り通話を発信する Lambda 関数の作成
SQS にメッセージが送信されたら、それを受け取って Twilio API を使って音声通話を発信する Lambda 関数を作成します。
ローカルでプロジェクトを作成する
ローカル環境で Lambda 関数のコードを作成します。Twilio の通話機能を利用するために外部ライブラリを使用する必要があり、Lambda コンソールのインラインエディタでは対応できないため、ローカルで zip にまとめてアップロードします。
mkdir slack-call-sender
cd slack-call-sender
npm init -y
npm install twilio
Lambda 関数の実装(SAFE_MODE・ホワイトリスト対応)
以下のコードでは、SQS から受け取ったメッセージをもとに Twilio の calls.create()
を使って自動通話を発信し、読み上げ内容を指定します。
あわせて以下の簡易的な安全対策も組み込んでいます:
- SAFE_MODE(環境変数) を
true
に設定すると Twilio への通話をスキップ - WHITELIST_PREFIX で指定した番号以外への発信をブロック
request_id
の取り扱いに触れつつ、冪等チェックの実装は省略(実装コメントにて示唆)
index.js
const twilio = require('twilio');
const client = twilio(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN
);
const SAFE_MODE = process.env.SAFE_MODE === 'true';
const WHITELIST_PREFIX = process.env.WHITELIST_PREFIX || '+8190';
exports.handler = async (event) => {
for (const record of event.Records) {
const body = JSON.parse(record.body);
const { to, message, request_id } = body;
// 簡易ホワイトリストチェック
if (!to.startsWith(WHITELIST_PREFIX)) {
console.warn(`🚫 Blocked call to non-whitelisted number: ${to}`);
continue;
}
// 検証時など SAFE_MODE 有効化で API 呼び出し回避
if (SAFE_MODE) {
console.log(`🛡️ SAFE_MODE: Skipping call to ${to}`);
continue;
}
// ※ request_id を使った冪等処理は必要に応じて別途設計してください
try {
await client.calls.create({
twiml: `<Response><Say language="ja-JP" voice="Polly.Mizuki">${message}</Say></Response>`,
to: to,
from: process.env.TWILIO_PHONE_NUMBER
});
console.log(`✅ 発信完了: ${to}`);
} catch (err) {
console.error(`❌ 通話発信失敗: ${err.message}`);
}
}
};
Lambda にデプロイする
Lambda にアップロードするため、プロジェクトを zip にまとめます。
zip -r function.zip .
AWS Lambda コンソール にアクセスし、「関数を作成」から新しい関数を作成します。ランタイムは Node.js 22.x を選び、名前は任意で OK です(ここでは slack-call-sender
とします)。
その後、コードタブで .zip ファイル
をアップロードします。
環境変数の設定
コード
タブ EXPLORER
内の ENVIRONMENT VARIABLES
を開き、 Add environment variables
ボタンをクリックして下記の環境変数を設定します。
キー | 値 |
---|---|
TWILIO_ACCOUNT_SID | Twilio のアカウント SID |
TWILIO_AUTH_TOKEN | Twilio の認証トークン |
TWILIO_PHONE_NUMBER | 発信元として使用する Twilio 電話番号(例:+815012345678) |
SAFE_MODE | true (課金防止のため初期は有効化を推奨) |
WHITELIST_PREFIX | +8190 (日本の携帯番号に限定) |
タイムアウト設定
Twilio の通話発信処理には数秒かかる場合があるため、Lambda のタイムアウト時間は最低でも 10 秒以上に設定しておくと安心です。
[設定] タブ → [一般設定] 編集ボタン → タイムアウト
テストイベントで動作確認する
Lambda コンソールの「テスト」タブから、以下のようなテストイベントを使って手動で動作確認を行うことができます。
{
"Records": [
{
"body": "{\"to\": \"+819999999999\", \"message\": \"認証コードは1234です\", \"request_id\": \"abc-123\"}"
}
]
}
SQS 関連ポリシーをロールにアタッチする
設定
タブを開き アクセス権限
セクションのロール名をクリックし、ロールの設定ページにジャンプします。
許可
タブの 許可ポリシー
の 許可を追加
から ポリシーをアタッチ
をクリックします。
AWSLambdaSQSQueueExecutionRole
を追加します。
Lambda のページに戻ります。
SQS からのアクセスを許可する
設定
タブの アクセス権限
から リソースベースのポリシーステートメント
に以下のアクセス権限を追加します。
項目 | 設定値 |
---|---|
ステートメント ID | AllowSQSInvoke |
プリンシパル | sqs.amazonaws.com |
アクション | lambda:InvokeFunction |
SQS の Lambda トリガーにアタッチする
SQS の設定画面に戻り、 Lambda トリガー
タブから作成したトリガーを設定します。
Slack リクエストを受け取り SQS に送信する Lambda 関数の作成
Slack のスラッシュコマンドから送信されるリクエストを受け取り、Amazon SQS にメッセージを送信する処理を AWS Lambda 上に実装します。この Lambda 関数は、API Gateway を通じて HTTP リクエストを受け取る構成とし、Twilio への通話処理は別の Lambda 関数で非同期に処理します。
Slack のスラッシュコマンドには 3 秒以内にレスポンスを返す必要 があるため、この Lambda 関数では Twilio API を呼ばず、SQS にメッセージを送信するだけにとどめます。
ローカルでプロジェクトを作成する
まずはローカル環境で Lambda 関数のコードを作成し、必要なパッケージをインストールします。
mkdir slack-call-request-handler
cd slack-call-request-handler
npm init -y
npm install aws-sdk
Lambda 関数の実装
次に、index.js
を作成し、Slack の署名検証と SQS へのメッセージ送信処理を実装します。以下のコードでは、Slack からのリクエストを受け取り、署名検証を行ったうえで、通話先番号と読み上げメッセージを SQS に送信します。
index.js
const crypto = require('crypto');
const querystring = require('querystring');
const { SQS } = require('aws-sdk');
const sqs = new SQS();
exports.handler = async (event) => {
const headers = Object.fromEntries(
Object.entries(event.headers || {}).map(([k, v]) => [k.toLowerCase(), v])
);
const timestamp = headers['x-slack-request-timestamp'];
const slackSignature = headers['x-slack-signature'];
if (!timestamp || !slackSignature) {
return { statusCode: 400, body: '❌ 必要なヘッダーがありません。' };
}
// リプレイ攻撃対策
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > 60 * 5) {
return { statusCode: 200, body: '❌ リクエストが古すぎます。' };
}
const rawBody = event.isBase64Encoded
? Buffer.from(event.body, 'base64').toString('utf8')
: event.body;
const sigBaseString = `v0:${timestamp}:${rawBody}`;
const mySignature = 'v0=' + crypto
.createHmac('sha256', process.env.SLACK_SIGNING_SECRET)
.update(sigBaseString, 'utf8')
.digest('hex');
// 署名検証
if (
!crypto.timingSafeEqual(
Buffer.from(mySignature, 'utf8'),
Buffer.from(slackSignature, 'utf8')
)
) {
return { statusCode: 200, body: '❌ 署名検証に失敗しました。' };
}
const parsedBody = querystring.parse(rawBody);
const text = parsedBody.text || '';
const [to, ...messageParts] = text.trim().split(/\s+/);
const message = messageParts.join(' ');
if (!to || !message) {
return {
statusCode: 200,
body: '❌ 入力形式が正しくありません。\n使用例: /voice +81XXXXXXXXXX 認証コードは1234です'
};
}
const sqsMessage = {
to,
message,
request_id: crypto.randomUUID()
};
try {
await sqs.sendMessage({
QueueUrl: process.env.SQS_QUEUE_URL,
MessageBody: JSON.stringify(sqsMessage)
}).promise();
return {
statusCode: 200,
body: ''
};
} catch (err) {
console.error('SQS error:', err);
return {
statusCode: 500,
body: '❌ SQS への送信に失敗しました。'
};
}
};
環境変数の設定
環境変数として、以下を Lambda 関数に設定してください。
キー | 値 |
---|---|
SQS_QUEUE_URL | 作成した SQS キューの URL |
SLACK_SIGNING_SECRET | Slack アプリの Signing Secret |
Lambda にデプロイする
Lambda にアップロードするため、プロジェクトを zip にまとめます。
zip -r function.zip .
AWS Lambda コンソール にアクセスし、「関数を作成」から新しい関数を作成します。ランタイムは Node.js 22.x を選び、名前は任意で OK です(ここでは slack-call-request-handler
とします)。その後、コードタブで .zip ファイル
をアップロードします。
環境変数の設定
コード
タブ EXPLORER
内の ENVIRONMENT VARIABLES
を開き、 Add environment variables
ボタンをクリックして下記の環境変数を設定します。
キー | 値 |
---|---|
SQS_QUEUE_URL | 作成した SQS キューの URL |
SLACK_SIGNING_SECRET | Slack アプリの Signing Secret |
SQS 関連ポリシーをロールにアタッチする
設定
タブを開き アクセス権限
セクションのロール名をクリックし、ロールの設定ページにジャンプします。 許可
タブの 許可ポリシー
の 許可を追加
から インラインポリシーを作成
をクリックします。
ポリシーエディタで JSON
を選択し、次の内容を入力して作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sqs:SendMessage",
"Resource": "arn:aws:sqs:****"
}
]
}
Lambda のページに戻ります。
テストイベントで動作確認する
Lambda 関数が正しく動作するかを確認するため、テストイベントを作成して動作確認を行うことができます。Lambda 関数の画面で テスト
タブを開き、 テストイベントの作成
を選択します。以下のような JSON を入力して、Slack からのリクエストを模擬します (署名検証をスキップする場合) 。
{
"headers": {
"X-Slack-Request-Timestamp": "1234567890",
"X-Slack-Signature": "v0=fake"
},
"body": "text=%2B819012345678+認証コードは1234です"
}
API Gateway の設定
Slack のスラッシュコマンドからのリクエストを受け取るために、AWS Lambda 関数に API Gateway をトリガーとして追加します。ここでは、Lambda コンソールから直接 HTTP API を作成する方法を紹介します。 トリガーの追加
ボタンをクリックします。
トリガータイプ
としてAPI Gateway
を選択します。API タイプ
はHTTP API
を選択します。API の作成方法
は新しい API
を選択します。セキュリティ
はオープン
(認証なし) を選択します。
トリガーが追加されると、API Gateway のエンドポイント URL が表示されます。この URL をコピーして控えておきます。後ほど Slack のスラッシュコマンドの設定で使用します。
API Gateway の設定を開き、左メニュー Integrations > 作成した API の ANY > 統合を管理
を選択します。 ペイロード形式のバージョン
を 1.0 に設定します。
まとめ
第 2 回では、Slack からのスラッシュコマンド /voice
を通じて、Twilio 経由で音声通話を自動で発信するためのバックエンド構成を実装しました。
本記事で構築したシステムは、次のような構成になっています。
- Slack のコマンド
/voice
により、通話先とメッセージが投稿される - API Gateway を通じてリクエストを受け取った Lambda 関数が Amazon SQS にメッセージを送信
- SQS をトリガーとする別の Lambda 関数 が Twilio の
Programmable Voice API
を呼び出し、音声通話を実行
この構成により、Slack の 3 秒制限を回避しつつ、Twilio の発信 API を安定して呼び出すことができます。
また、Twilio 課金リスクに配慮し、
- 検証時に課金を防ぐ
SAFE_MODE
- 特定の番号以外をブロックする
WHITELIST_PREFIX
といった対策も取り入れました。
次回(第 3 回)では、Slack アプリの設定手順(スラッシュコマンドの作成や署名シークレットの取得など)と、実際の動作確認を解説します。
- 第 1 回: 構成と Twilio 初期設定
- 第 3 回: Slack アプリの設定と動作確認