class-restai-comments.php
1 <?php 2 /** 3 * AI-assisted comment moderation. Hooked into pre_comment_approved so the 4 * decision is made before the comment is committed. 5 * 6 * @package RESTai 7 */ 8 9 namespace RESTai; 10 11 if ( ! defined( 'ABSPATH' ) ) { 12 exit; 13 } 14 15 class Comments { 16 17 /** @var Client */ 18 private $client; 19 20 public function __construct( Client $client ) { 21 $this->client = $client; 22 add_filter( 'pre_comment_approved', array( $this, 'moderate' ), 9, 2 ); 23 } 24 25 public function moderate( $approved, $commentdata ) { 26 $settings = get_option( 'restai_settings', array() ); 27 if ( empty( $settings['enable_moderation'] ) ) { 28 return $approved; 29 } 30 // Already in spam/trash — don't waste a call. 31 if ( in_array( $approved, array( 'spam', 'trash' ), true ) ) { 32 return $approved; 33 } 34 35 $plugin = Plugin::instance(); 36 $project_id = $plugin->provisioner->project_for( 'comment_moderator' ); 37 if ( $project_id <= 0 ) { 38 return $approved; 39 } 40 41 $author = isset( $commentdata['comment_author'] ) ? $commentdata['comment_author'] : ''; 42 $body = isset( $commentdata['comment_content'] ) ? $commentdata['comment_content'] : ''; 43 44 $prompt = "AUTHOR: {$author}\nCOMMENT:\n{$body}"; 45 $resp = $this->client->ask( $project_id, $prompt ); 46 if ( is_wp_error( $resp ) ) { 47 return $approved; 48 } 49 50 $json = $this->parse_json( $resp ); 51 if ( ! is_array( $json ) ) { 52 return $approved; 53 } 54 55 if ( ! empty( $json['spam'] ) ) { 56 return 'spam'; 57 } 58 if ( ! empty( $json['toxic'] ) ) { 59 return 0; // hold for moderation. 60 } 61 62 // Stash the analysis on the post for the moderator to inspect later. 63 add_action( 'comment_post', function ( $comment_id ) use ( $json ) { 64 update_comment_meta( $comment_id, '_restai_analysis', $json ); 65 }, 10, 1 ); 66 67 return $approved; 68 } 69 70 private function parse_json( $text ) { 71 $text = trim( (string) $text ); 72 if ( '' === $text ) { 73 return null; 74 } 75 if ( preg_match( '/```(?:json)?\s*(\{.*?\})\s*```/s', $text, $m ) ) { 76 $d = json_decode( $m[1], true ); 77 if ( is_array( $d ) ) { 78 return $d; 79 } 80 } 81 $stripped = preg_replace( '/^```(?:[a-z]+)?\s*/i', '', $text ); 82 $stripped = preg_replace( '/\s*```$/', '', $stripped ); 83 $d = json_decode( $stripped, true ); 84 if ( is_array( $d ) ) { 85 return $d; 86 } 87 $start = strpos( $stripped, '{' ); 88 while ( false !== $start ) { 89 $depth = 0; 90 $len = strlen( $stripped ); 91 for ( $i = $start; $i < $len; $i++ ) { 92 if ( $stripped[ $i ] === '{' ) { 93 $depth++; 94 } elseif ( $stripped[ $i ] === '}' ) { 95 $depth--; 96 if ( 0 === $depth ) { 97 $d = json_decode( substr( $stripped, $start, $i - $start + 1 ), true ); 98 if ( is_array( $d ) ) { 99 return $d; 100 } 101 break; 102 } 103 } 104 } 105 $start = strpos( $stripped, '{', $start + 1 ); 106 } 107 return null; 108 } 109 }