2026年8月17日
2026年8月17日
WordPressのイベントをSlack・Discordにリアルタイム通知する方法
はじめに
WordPressサイトで新しい記事が公開された、コメントが投稿された、不正ログインの試みがあったという情報をリアルタイムでSlackやDiscordに通知したいケースは多いです。WordPress Webhookとwp_remote_post()を組み合わせることで、プラグインなしでこれらの通知を実装できます。本記事では実用的な通知システムの構築方法を解説します。
症状・原因
- 新しい投稿が公開されてもチームへの周知が遅れ、SNSシェアのタイミングを逃している
- 不正ログインの試みが繰り返されていることに気づかずにいる
- コメントのスパムや不審なコメントへの対応が遅れている
解決手順
ステップ1:Slack Incoming Webhookを設定してテストする
# 1. Slack App作成: https://api.slack.com/apps
# 2. "Incoming Webhooks"を有効化
# 3. "Add New Webhook to Workspace"でチャンネルを選択
# 4. Webhook URLをコピー
# curlでWebhookのテスト(コマンドライン)
curl -X POST \
-H 'Content-type: application/json' \
--data '{"text":"WordPressからのテスト通知 :wordpress:"}' \
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXX
# 成功時のレスポンス
# ok
<?php
// wp-config.phpまたは環境変数でWebhook URLを管理
// (セキュリティのためfunctions.phpにハードコードしない)
define( 'SLACK_WEBHOOK_URL', 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL' );
define( 'DISCORD_WEBHOOK_URL', 'https://discord.com/api/webhooks/YOUR/WEBHOOK/URL' );
ステップ2:新規投稿公開時にSlack通知を送る
<?php
// publish_postアクション(投稿が公開されたときに発火)
add_action( 'publish_post', 'notify_slack_on_new_post', 10, 2 );
function notify_slack_on_new_post( $post_id, $post ) {
// 自動保存と一括更新を除外
if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
return;
}
// 既に通知済みかチェック(二重通知を防ぐ)
if ( get_post_meta( $post_id, '_slack_notified', true ) ) {
return;
}
// 投稿情報を取得
$post_url = get_permalink( $post_id );
$post_title = get_the_title( $post_id );
$author_name = get_the_author_meta( 'display_name', $post->post_author );
$category = implode( ', ', wp_get_post_categories( $post_id, array( 'fields' => 'names' ) ) );
// Slackのブロックメッセージ形式で送信
$payload = array(
'blocks' => array(
array(
'type' => 'header',
'text' => array(
'type' => 'plain_text',
'text' => '新しい記事が公開されました',
),
),
array(
'type' => 'section',
'fields' => array(
array( 'type' => 'mrkdwn', 'text' => "*タイトル*\n{$post_title}" ),
array( 'type' => 'mrkdwn', 'text' => "*著者*\n{$author_name}" ),
array( 'type' => 'mrkdwn', 'text' => "*カテゴリ*\n{$category}" ),
array( 'type' => 'mrkdwn', 'text' => "*URL*\n<{$post_url}|記事を見る>" ),
),
),
),
);
// Slackに送信
$result = send_webhook_notification( SLACK_WEBHOOK_URL, $payload );
if ( $result ) {
// 通知済みフラグを保存(二重通知防止)
update_post_meta( $post_id, '_slack_notified', current_time( 'mysql' ) );
}
}
// Webhook送信の共通関数
function send_webhook_notification( $webhook_url, $payload ) {
$response = wp_remote_post(
$webhook_url,
array(
'method' => 'POST',
'timeout' => 10,
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode( $payload ),
)
);
if ( is_wp_error( $response ) ) {
error_log( 'Webhook Error: ' . $response->get_error_message() );
return false;
}
return 200 === wp_remote_retrieve_response_code( $response );
}
ステップ3:新規コメント投稿時にSlack通知を送る
<?php
// comment_postアクション(コメントが保存されたときに発火)
add_action( 'comment_post', 'notify_slack_on_comment', 10, 3 );
function notify_slack_on_comment( $comment_id, $comment_approved, $commentdata ) {
// スパムコメントは通知しない
if ( 'spam' === $comment_approved ) {
return;
}
$comment = get_comment( $comment_id );
$post = get_post( $comment->comment_post_ID );
$post_url = get_permalink( $comment->comment_post_ID );
// 承認待ちと承認済みで絵文字を変える
$status_emoji = ( 1 == $comment_approved ) ? ':white_check_mark:' : ':clock3:';
$status_text = ( 1 == $comment_approved ) ? '承認済み' : '承認待ち';
$payload = array(
'text' => sprintf(
'%s 新しいコメント(%s)' . "\n" .
'投稿: <%s|%s>' . "\n" .
'投稿者: %s <%s>' . "\n" .
'コメント: %s',
$status_emoji,
$status_text,
$post_url,
$post->post_title,
esc_html( $comment->comment_author ),
esc_html( $comment->comment_author_email ),
wp_trim_words( $comment->comment_content, 20, '...' )
),
);
send_webhook_notification( SLACK_WEBHOOK_URL, $payload );
}
ステップ4:Discord Webhookの異なるペイロード形式で通知する
<?php
// Discordのembedフォーマット(Slackと形式が異なる)
function send_discord_notification( $title, $description, $url = '', $color = 3447003 ) {
// Discordのembed形式
$payload = array(
'embeds' => array(
array(
'title' => $title,
'description' => $description,
'url' => $url,
'color' => $color, // 16進数の色コード(3447003 = #3498DB)
'footer' => array(
'text' => get_bloginfo( 'name' ) . ' | ' . current_time( 'Y-m-d H:i' ),
),
),
),
);
return send_webhook_notification( DISCORD_WEBHOOK_URL, $payload );
}
// 投稿公開時にDiscordにも通知
add_action( 'publish_post', 'notify_discord_on_new_post', 10, 2 );
function notify_discord_on_new_post( $post_id, $post ) {
if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
return;
}
$title = get_the_title( $post_id );
$url = get_permalink( $post_id );
$excerpt = get_the_excerpt( $post_id ) ?: wp_trim_words( $post->post_content, 30 );
$author = get_the_author_meta( 'display_name', $post->post_author );
send_discord_notification(
'📝 新しい記事: ' . $title,
"**著者**: {$author}\n**概要**: {$excerpt}",
$url,
0x0073AA // WordPressブルー
);
}
ステップ5:ログイン失敗通知でセキュリティ監視を行う
<?php
// wp_login_failedアクション(ログイン失敗時に発火)
add_action( 'wp_login_failed', 'notify_failed_login', 10, 2 );
function notify_failed_login( $username, $error ) {
// IPアドレスを取得(プロキシ対応)
$ip_address = '';
foreach ( array( 'HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR' ) as $key ) {
if ( ! empty( $_SERVER[ $key ] ) ) {
$ip_address = sanitize_text_field( $_SERVER[ $key ] );
break;
}
}
// 同一IPからの失敗回数をTransientで追跡
$fail_key = 'login_fail_' . md5( $ip_address );
$fail_count = (int) get_transient( $fail_key );
$fail_count++;
set_transient( $fail_key, $fail_count, HOUR_IN_SECONDS );
// 5回以上失敗したときだけ通知(スパム防止)
if ( $fail_count < 5 ) {
return;
}
$message = sprintf(
':warning: *ログイン失敗アラート*' . "\n" .
'ユーザー名: `%s`' . "\n" .
'IPアドレス: `%s`' . "\n" .
'失敗回数: *%d回*(過去1時間)' . "\n" .
'時刻: %s',
esc_html( $username ),
esc_html( $ip_address ),
$fail_count,
current_time( 'Y-m-d H:i:s' )
);
send_webhook_notification( SLACK_WEBHOOK_URL, array( 'text' => $message ) );
// 10回以上でDiscordにも通知(より深刻な警告として)
if ( $fail_count >= 10 ) {
send_discord_notification(
'⚠️ ブルートフォース攻撃の可能性',
"IP: {$ip_address}\nユーザー名: {$username}\n失敗回数: {$fail_count}回/時",
'',
0xFF0000 // 赤
);
}
}
注意事項
- Webhook URLの秘匿: SlackとDiscordのWebhook URLはwp-config.phpの定数または環境変数で管理し、リポジトリにコミットしないこと
- 二重通知防止:
publish_postは記事の更新でも発火するため、_slack_notifiedメタフィールドで通知済み管理が必要です - レート制限: Discordは30秒に50リクエストの制限があります。大量の通知が発生する環境ではキューイングを検討してください
- タイムアウト:
wp_remote_post()のタイムアウトを短く設定し(5〜10秒)、ページ表示速度への影響を最小化してください
まとめ
WordPressのアクションフック(publish_post、comment_post、wp_login_failed)とwp_remote_post()を組み合わせることで、Slack・Discord両方へのリアルタイム通知システムを構築できます。ログイン失敗の監視はセキュリティ強化にも有効です。関連記事:WordPressのセキュリティを強化する方法