WordPressのログイン・ログアウト・プラグイン操作をChatwork・Slack・メールに通知するプラグインの作り方(ソース全文あり)

概要

このページでは、以下のイベントを検知してChatwork / Slack / メールへ通知するWordPressのカスタムプラグインを構築する方法を紹介します。

  • ユーザーのログイン成功
  • ユーザーのログアウト
  • ログイン失敗(認証エラー)
  • プラグインの有効化/無効化

通知の送信先ごとに通知対象イベントを細かく制御可能で、実運用に対応した柔軟な設計です。


主な機能

通知対象イベント

  • ログイン成功
  • ログアウト
  • ログイン失敗
  • プラグインの有効化/無効化

通知先

  • Chatwork
  • Slack
  • メール

通知制御

  • 各通知先ごとに通知するイベントをON/OFF可能
  • 通知対象ロールの絞り込み(例: 管理者のみ)
  • 通知除外ユーザーの設定(例: 特定の開発者など)

ファイル構成

/wp-content/plugins/multi-login-notifier-extended/
├── multi-login-notifier-extended.php
└── includes/
    └── settings.php

ソースコード全文

multi-login-notifier-extended.php

<?php
/**
 * Plugin Name: Multi Login Notifier Extended
 * Description: WordPressのログイン成功・失敗・ログアウト・プラグインの有効化/無効化をChatwork/Slack/メールで通知。通知種別の制御も可能。
 * Version: 1.5.0
 * Author: Your Name
 */

defined('ABSPATH') || exit;

require_once plugin_dir_path(__FILE__) . 'includes/settings.php';

/**
 * ログイン成功通知
 */
add_action('wp_login', function ($user_login, $user) {
    if (mln_should_skip_notification($user_login, $user->roles)) return;

    $msg = mln_build_message('ログイン成功通知', $user_login, $user->roles);
    mln_send_notifications([
        'type'  => 'login',
        'title' => "ログイン成功: {$user_login}",
        'body'  => $msg,
    ]);
}, 10, 2);

/**
 * ログアウト通知(Cookieからユーザー名を取得)
 */
add_action('wp_logout', function () {
    if (isset($_COOKIE[LOGGED_IN_COOKIE])) {
        $cookie_parts = explode('|', $_COOKIE[LOGGED_IN_COOKIE]);
        $username = $cookie_parts[0] ?? null;

        if ($username) {
            $user = get_user_by('login', $username);
            if ($user && !mln_should_skip_notification($user->user_login, $user->roles)) {
                $msg = mln_build_message('ログアウト通知', $user->user_login, $user->roles);
                mln_send_notifications([
                    'type'  => 'logout',
                    'title' => "ログアウト: {$user->user_login}",
                    'body'  => $msg,
                ]);
            }
        }
    }
});

/**
 * ログイン失敗通知
 */
add_action('wp_login_failed', function ($username) {
    $site = get_bloginfo('name');
    $url  = home_url();
    $ip   = $_SERVER['REMOTE_ADDR'] ?? '';
    $time = current_time('mysql');

    $msg = "[ログイン失敗通知]\n"
         . "ユーザー名: {$username}\n"
         . "日時: {$time}\n"
         . "サイト: {$site}\n"
         . "URL: {$url}\n"
         . "IPアドレス: {$ip}";

    mln_send_notifications([
        'type'  => 'fail',
        'title' => "ログイン失敗: {$username}",
        'body'  => $msg,
    ]);
});

/**
 * プラグイン操作通知
 */
register_activation_hook(__FILE__, function () {
    if (!function_exists('get_plugin_data')) require_once ABSPATH . 'wp-admin/includes/plugin.php';
    $data = get_plugin_data(__FILE__);
    mln_notify_plugin_status_change($data['Name'], $data['Version'], '有効化');
});

add_action('activated_plugin', function ($plugin_path) {
    if ($plugin_path === plugin_basename(__FILE__)) return;
    mln_handle_plugin_event($plugin_path, '有効化');
});
add_action('deactivated_plugin', function ($plugin_path) {
    if ($plugin_path === plugin_basename(__FILE__)) return;
    mln_handle_plugin_event($plugin_path, '無効化');
});

/**
 * ユーザー除外・ロール制御
 */
function mln_should_skip_notification($username, $roles) {
    $excluded_users = array_map('trim', explode(',', get_option('mln_excluded_users', '')));
    if (in_array($username, $excluded_users, true)) return true;

    $allowed_roles = array_map('trim', explode(',', get_option('mln_allowed_roles', '')));
    if (!empty($allowed_roles) && !array_intersect($roles, $allowed_roles)) return true;

    return false;
}

/**
 * 通知メッセージ生成
 */
function mln_build_message($type, $username, $roles) {
    $site = get_bloginfo('name');
    $url  = home_url();
    $ip   = $_SERVER['REMOTE_ADDR'] ?? '';
    $time = current_time('mysql');
    $roles_text = implode(', ', $roles);

    return "[{$type}]\n"
         . "ユーザー: {$username}\n"
         . "権限: {$roles_text}\n"
         . "日時: {$time}\n"
         . "サイト: {$site}\n"
         . "URL: {$url}\n"
         . "IPアドレス: {$ip}";
}

/**
 * プラグイン操作通知本体
 */
function mln_handle_plugin_event($plugin_path, $status = '有効化') {
    if (!function_exists('get_plugin_data')) require_once ABSPATH . 'wp-admin/includes/plugin.php';
    $plugin_file = WP_PLUGIN_DIR . '/' . $plugin_path;
    if (!file_exists($plugin_file)) return;
    $plugin_data = get_plugin_data($plugin_file);
    mln_notify_plugin_status_change($plugin_data['Name'], $plugin_data['Version'], $status);
}

function mln_notify_plugin_status_change($name, $version, $status = '有効化') {
    $site = get_bloginfo('name');
    $url  = home_url();
    $time = current_time('mysql');

    $msg = "[プラグイン{$status}通知]\n"
         . "サイト: {$site}\n"
         . "プラグイン: {$name} ({$version})\n"
         . "URL: {$url}\n"
         . "時間: {$time}";

    mln_send_notifications([
        'type'  => 'plugin',
        'title' => "プラグイン{$status}: {$name}",
        'body'  => $msg,
    ]);
}

/**
 * 通知の送信(種別別に送信先制御)
 */
function mln_send_notifications($params) {
    $type  = $params['type'];  // login / logout / fail / plugin
    $body  = $params['body'];
    $title = $params['title'];

    // Chatwork
    if (
        get_option('mln_chatwork_enabled') === '1' &&
        get_option("mln_chatwork_notify_{$type}") === '1'
    ) {
        $token = get_option('mln_chatwork_token');
        $room  = get_option('mln_chatwork_room_id');
        if ($token && $room) {
            wp_remote_post("https://api.chatwork.com/v2/rooms/{$room}/messages", [
                'headers' => ['X-ChatWorkToken' => $token],
                'body'    => ['body' => $body],
            ]);
        }
    }

    // Slack
    if (
        get_option('mln_slack_enabled') === '1' &&
        get_option("mln_slack_notify_{$type}") === '1'
    ) {
        $webhook = get_option('mln_slack_webhook_url');
        if ($webhook) {
            wp_remote_post($webhook, [
                'headers' => ['Content-Type' => 'application/json'],
                'body'    => json_encode(['text' => $body]),
            ]);
        }
    }

    // Email
    if (
        get_option('mln_email_enabled') === '1' &&
        get_option("mln_email_notify_{$type}") === '1'
    ) {
        $to = get_option('mln_email_to');
        if ($to) {
            wp_mail($to, $title, $body);
        }
    }
}

includes/settings.php

<?php
defined('ABSPATH') || exit;

add_action('admin_menu', function () {
    add_options_page(
        'Login Notifier Settings',
        'Login Notifier',
        'manage_options',
        'mln-settings',
        'mln_render_settings_page'
    );
});

function mln_render_settings_page() {
    ?>
    <div class="wrap">
        <h1>Multi Login Notifier Extended 設定</h1>
        <form method="post" action="options.php">
            <?php
            settings_fields('mln_settings');
            do_settings_sections('mln-settings');
            submit_button();
            ?>
        </form>
    </div>
    <?php
}

add_action('admin_init', function () {
    // 共通設定
    register_setting('mln_settings', 'mln_allowed_roles');
    register_setting('mln_settings', 'mln_excluded_users');

    // Chatwork
    register_setting('mln_settings', 'mln_chatwork_enabled');
    register_setting('mln_settings', 'mln_chatwork_token');
    register_setting('mln_settings', 'mln_chatwork_room_id');
    register_setting('mln_settings', 'mln_chatwork_notify_login');
    register_setting('mln_settings', 'mln_chatwork_notify_logout');
    register_setting('mln_settings', 'mln_chatwork_notify_fail');
    register_setting('mln_settings', 'mln_chatwork_notify_plugin');

    // Slack
    register_setting('mln_settings', 'mln_slack_enabled');
    register_setting('mln_settings', 'mln_slack_webhook_url');
    register_setting('mln_settings', 'mln_slack_notify_login');
    register_setting('mln_settings', 'mln_slack_notify_logout');
    register_setting('mln_settings', 'mln_slack_notify_fail');
    register_setting('mln_settings', 'mln_slack_notify_plugin');

    // Email
    register_setting('mln_settings', 'mln_email_enabled');
    register_setting('mln_settings', 'mln_email_to');
    register_setting('mln_settings', 'mln_email_notify_login');
    register_setting('mln_settings', 'mln_email_notify_logout');
    register_setting('mln_settings', 'mln_email_notify_fail');
    register_setting('mln_settings', 'mln_email_notify_plugin');

    // セクション
    add_settings_section('mln_main', '通知設定', null, 'mln-settings');

    // Chatwork 設定
    mln_add_checkbox('mln_chatwork_enabled', 'Chatwork通知を有効化');
    mln_add_text('mln_chatwork_token', 'Chatwork APIトークン');
    mln_add_text('mln_chatwork_room_id', 'ChatworkルームID');
    mln_add_checkbox('mln_chatwork_notify_login', 'ログイン成功通知');
    mln_add_checkbox('mln_chatwork_notify_logout', 'ログアウト通知');
    mln_add_checkbox('mln_chatwork_notify_fail', 'ログイン失敗通知');
    mln_add_checkbox('mln_chatwork_notify_plugin', 'プラグイン操作通知');

    // Slack 設定
    mln_add_checkbox('mln_slack_enabled', 'Slack通知を有効化');
    mln_add_text('mln_slack_webhook_url', 'Slack Webhook URL');
    mln_add_checkbox('mln_slack_notify_login', 'ログイン成功通知');
    mln_add_checkbox('mln_slack_notify_logout', 'ログアウト通知');
    mln_add_checkbox('mln_slack_notify_fail', 'ログイン失敗通知');
    mln_add_checkbox('mln_slack_notify_plugin', 'プラグイン操作通知');

    // メール通知設定
    mln_add_checkbox('mln_email_enabled', 'メール通知を有効化');
    mln_add_text('mln_email_to', '通知先メールアドレス');
    mln_add_checkbox('mln_email_notify_login', 'ログイン成功通知');
    mln_add_checkbox('mln_email_notify_logout', 'ログアウト通知');
    mln_add_checkbox('mln_email_notify_fail', 'ログイン失敗通知');
    mln_add_checkbox('mln_email_notify_plugin', 'プラグイン操作通知');

    // ユーザー制御設定
    mln_add_text('mln_allowed_roles', '通知対象のロール(カンマ区切り)');
    mln_add_text('mln_excluded_users', '通知除外ユーザー名(カンマ区切り)');
});

/**
 * 入力欄ヘルパー(テキスト)
 */
function mln_add_text($option_name, $label) {
    add_settings_field($option_name, $label, function () use ($option_name) {
        $value = esc_attr(get_option($option_name));
        echo "<input type='text' name='{$option_name}' value='{$value}' class='regular-text' />";
    }, 'mln-settings', 'mln_main');
}

/**
 * 入力欄ヘルパー(チェックボックス)
 */
function mln_add_checkbox($option_name, $label) {
    add_settings_field($option_name, $label, function () use ($option_name) {
        $value = get_option($option_name);
        echo "<input type='checkbox' name='{$option_name}' value='1' " . checked(1, $value, false) . " />";
    }, 'mln-settings', 'mln_main');
}

設定画面の説明

設定画面では、以下の項目を管理できます。

  • Chatwork: トークン、ルームID、通知種別のON/OFF
  • Slack: Webhook URL、通知種別のON/OFF
  • メール: 通知先アドレス、通知種別のON/OFF
  • 通知対象ロール、通知除外ユーザーの指定

通知メッセージのフォーマット

[ログイン成功通知]
ユーザー: example_user
権限: administrator
日時: 2025-06-04 10:23:45
サイト: サイト名
URL: https://example.com
IPアドレス: 123.45.67.89

注意点

  • Slack通知には有効なWebhook URLが必要です
  • Chatwork通知にはAPIトークンとルームIDが必要です
  • メール送信は wp_mail() が有効である必要があります

今後の拡張案

  • 通知ログのデータベース保存
  • IPやUAなどによる通知条件の強化
  • JSONでのバックアップ記録機能

まとめ

本プラグインは、ログイン状況やプラグインの有効化・無効化などの操作を監視し、Chatwork・Slack・メールでリアルタイムに通知したいケースに役立ちます。

通知先ごとに通知対象を制御できるため、複数の運用担当者やシステム管理者が関与するサイトにも最適です。

コメント

タイトルとURLをコピーしました