blogスタッフブログ
HOME > スタッフブログ > CMS > PowerCMS X >PowerCMS Xフォームメールの独自フィルタにフィードバック機能を実装する

PowerCMS Xフォームメールの独自フィルタにフィードバック機能を実装する

過去の記事でPowerCMS Xプラグインで機械学習(ベイジアンフィルタ)の実装を紹介してきましたが、弊社問い合わせフォームのメールフィルタとして導入しました。一般的なスパムフィルタと異なり独自に学習させるため、メール内容から(お客様からの)お問い合わせメールと迷惑メールを判別可能です。

学習量が増えるとフィルタ精度が上がるため、メールを読むのと同時にフィードバック(学習)できたらと考えました。今回はPowerCMS Xプラグインでフィードバック機能の実装例を紹介します。

フィードバック機能実装

前回の記事でも追加したRESTful APIのエンドポイントとして実装します。config.jsonとプラグインのphp(NaiveBayes.php)ファイルに追記します。

config.json

"api_methods": {
    "v1": {
        "train": {
            "component": "NaiveBayes",
            "method": "api_endpoint_train",
            "requires_login": false,
            "allowed": [
                "GET",
                "POST"
            ]
        }
    }
},
"callbacks": {
    "naivebayes_post_save_contact": {
        "contact": {
            "post_save": {
                "component": "NaiveBayes",
                "priority": 5,
                "method": "post_save_contact"
            }
        }
    }
},
"settings": {
    "naivebayes_question_id": ""
},
"cfg_template": "cfg_template.tmpl",
"cfg_system": 1,
"cfg_space": 1

メールから気軽にフィードバックできるようログイン不要にしますが、不正・重複登録を防ぐためpost_saveコールバックで(フォームの内容保存時に)tokenを発行して認証に利用します。また、プラグイン設定で学習内容(が入力される設問ID)を指定するようにします。

NaiveBayes.php

/**
 * コンタクト保存時のコールバック
 *
 * @param object $cb コールバックオブジェクト
 * @param Prototype $app アプリケーション
 * @param object $obj 保存後のコンタクトオブジェクト
 * @param object $original 保存前のコンタクトオブジェクト
 * 
 * @return bool
 */
public function post_save_contact(&$cb, $app, &$obj, $original) {
    $ctx = $app->ctx;
    $formId = $app->param('form_id');
    if (!$formId) {
        return true;
    }
    $form = $app->db->model('form')->load((int) $formId);
    $workspaceId = $form->workspace ? $form->workspace->id : 0;

    // API認証用にセッション生成
    $token = $app->magic();
    $session = $app->db->model('session')->get_by_key([
        'name' => $token,
        'kind' => 'CR'
    ]);
    $session->start($app->request_time);
    $session->key('bayes_train');
    $session->value($obj->id);
    $session->workspace_id($workspaceId);
    $sessionExpires = 86400 * 7;
    $session->expires($app->request_time + $sessionExpires);
    $session->save();

    // メールテンプレートで利用するため変数に設定
    $ctx->vars['contact_token'] = $session->name;
    
    return true;
}

/**
 * RESTful API エンドポイント train
 * API経由でパラメータの値から学習
 * 
 * @param PTRESTfulAPIv1 $app アプリケーション
 */
function api_endpoint_train($app) {
    $json = [];
    $category = $app->param('category');
    $class = $app->param('class');
    $token = $app->param('token');
    $contactId = $app->param('contact_id');
    $workspaceId = $app->param('workspace_id') ?? 0;
    $questionId = $this->get_config_value('naivebayes_question_id', $workspaceId);

    if (!$questionId) {
        $json['status'] = 'error';
        $json['message'] = '設問IDが指定されていません。プラグイン設定で設問IDを設定してください。';
    } else if (!$token || !$contactId) {
        $json['status'] = 'error';
        $json['message'] = 'パラメータが指定されていません。tokenでトークン、contact_idでコンタクトIDを指定してください。';
    } else if ($category && $class) {
        $this->setEnablePosIds();
        $this->getSmParam($app);

        // セッションを取得
        $session = $app->db->model('session')->load([
            'name' => $token,
            'key' => 'bayes_train',
            'value' => $contactId
        ]);
        $session = $session[0] ?? null;

        // 学習テキストを取得
        $text = '';
        $questionName = 'question_' . $questionId;
        $contact = $app->db->model('contact')->load([
            'contact_id' => $contactId,
            'workspace_id' => $workspaceId
        ]);
        $contact = $contact[0] ?? null;
        if ($contact && $contact->data) {
            $contactData = json_decode($contact->data);
            if ($contactData->$questionName) {
                $text = trim($contactData->$questionName);
            }
        }

        // 有効なセッションあり
        if ($session && $text) {
            // ベイズ文章オブジェクトを生成
            $app->get_scheme_from_db('bayes_term_block');
            $termBlock = $app->db->model('bayes_term_block')->new();
            $termBlock->texts($text);
            $termBlock->category($category);
            $termBlock->class($class);
            $termBlock->workspace_id($workspaceId);
            $app->set_default($termBlock);
            $termBlock->save();

            // 学習
            if ($this->train($app, $termBlock, null)) {
                $json['status'] = 'success';
                $json['message'] = '学習に成功しました。';
                $json['text'] = $text;
                $json['category'] = $category;
                $json['class'] = $class;

                // セッション削除(重複登録防止)
                $session->remove();
            } else {
                $json['status'] = 'error';
                $json['message'] = '学習に失敗しました。';
            }
        } else {
            $json['status'] = 'error';
            $json['message'] = '有効なセッションがありません。';    
        }
    } else {
        $json['status'] = 'error';
        $json['message'] = 'パラメータが指定されていません。categoryでカテゴリ、classで分類を指定してください。';
    }

    $app->print_json($json);
}

APIの利用例

/api/ でAPIを設定している場合、/api/APIバージョン/ワークスペースID/train?category=フィルタカテゴリ&class=フィルタ分類&token=トークン&contact_id=コンタクトID にアクセスするとフィードバックできます。ここで先ほどpost_saveコールバックで発行したtokenを利用して認証しています。

まとめ

authenticationエンドポイントでログイン認証するのが一般的ですが、PowerCMS Xの機能を組み合わせることで1クリックで完了するフィードバック機能が実装できました。また、tokenはフィードバック完了時に削除するため、重複登録が避けられるメリットもあります。

関連記事

最近の記事

カテゴリ

アーカイブ

スタッフ別