PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 0.9.2
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v0.9.2
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
desktop-mode / includes / comments-window / spam-score.php

spam-score.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 0.9.2, at includes/comments-window/spam-score.php

149 lines 4.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Desktop Mode — Native Comments Window: spam confidence scoring.
4 *
5 * Returns a 0–100 integer for every comment row exposing how likely
6 * the framework thinks the comment is spam. The default heuristics
7 * are intentionally cheap (no external API calls):
8 *
9 * - +35 Akismet flagged this comment as spam.
10 * - +25 Comment is in the 'spam' status.
11 * - +20 Author's prior spam rate ≥ 20%.
12 * - +15 Comment contains 4+ links.
13 * - +10 Comment matches the disallowed-keys list.
14 * - +10 Comment is from an unauthenticated author with no
15 * previously-approved comment.
16 *
17 * The score caps at 100 and floors at 0. Sites can shape this score
18 * via the `desktop_mode_comments_window_spam_score` filter — that's
19 * where the AI fallback should hook when the site has no Akismet
20 * but does have an AI provider configured.
21 *
22 * @package WPDesktopMode
23 * @since 0.19.0
24 */
25
26 defined( 'ABSPATH' ) || exit;
27
28 /**
29 * Compute a 0–100 spam confidence score for a comment.
30 *
31 * @since 0.19.0
32 *
33 * @param int|WP_Comment $comment Comment id or object.
34 * @return int 0–100. Higher = more spam-like.
35 */
36 function desktop_mode_comments_window_spam_score( $comment ) {
37 $comment = get_comment( $comment );
38 if ( ! $comment instanceof WP_Comment ) {
39 return 0;
40 }
41
42 $score = 0;
43
44 // Akismet — if installed, its verdict is the strongest signal we have.
45 $akismet_result = (string) get_comment_meta( $comment->comment_ID, 'akismet_result', true );
46 if ( 'true' === $akismet_result ) {
47 $score += 35;
48 }
49
50 // In-spam status — already-decided spam ranks highest.
51 if ( 'spam' === wp_get_comment_status( $comment ) ) {
52 $score += 25;
53 }
54
55 // Author's prior spam rate (only when the author has 3+ comments to base it on).
56 $author_email = (string) $comment->comment_author_email;
57 if ( '' !== $author_email ) {
58 $prior_spam = (int) get_comments(
59 array(
60 'author_email' => $author_email,
61 'status' => 'spam',
62 'count' => true,
63 )
64 );
65 $prior_total = (int) get_comments(
66 array(
67 'author_email' => $author_email,
68 'status' => 'all',
69 'count' => true,
70 )
71 );
72 if ( $prior_total >= 3 ) {
73 $rate = $prior_spam / $prior_total;
74 if ( $rate >= 0.5 ) {
75 $score += 30;
76 } elseif ( $rate >= 0.2 ) {
77 $score += 20;
78 }
79 }
80 }
81
82 // Link count — 4+ links is the classic SEO spam signature.
83 $link_count = preg_match_all( '#https?://#i', (string) $comment->comment_content );
84 if ( $link_count >= 4 ) {
85 $score += 15;
86 } elseif ( $link_count >= 2 ) {
87 $score += 5;
88 }
89
90 // Disallowed keys (option 'disallowed_keys', the modern name for
91 // what used to be the comment blacklist).
92 $disallowed = (string) get_option( 'disallowed_keys', '' );
93 if ( '' !== trim( $disallowed ) ) {
94 $keys = array_filter( array_map( 'trim', explode( "\n", $disallowed ) ) );
95 foreach ( $keys as $key ) {
96 if ( '' === $key ) {
97 continue;
98 }
99 if ( false !== stripos( $comment->comment_content, $key )
100 || false !== stripos( $comment->comment_author, $key )
101 || false !== stripos( $comment->comment_author_email, $key )
102 || false !== stripos( $comment->comment_author_url, $key )
103 ) {
104 $score += 10;
105 break;
106 }
107 }
108 }
109
110 // Unauthenticated + no prior approved comment.
111 if ( 0 === (int) $comment->user_id ) {
112 $prior_approved = (int) get_comments(
113 array(
114 'author_email' => $author_email,
115 'status' => 'approve',
116 'count' => true,
117 )
118 );
119 if ( 0 === $prior_approved ) {
120 $score += 10;
121 }
122 }
123
124 // Clamp into the documented range BEFORE the filter so a runaway
125 // custom hook can't push it past 100. The filter is allowed to
126 // further clamp DOWN (e.g. force 0 for an allowlisted author).
127 $score = max( 0, min( 100, $score ) );
128
129 /**
130 * Filter the computed spam confidence score for a comment.
131 *
132 * Hook here to plug in an AI fallback when Akismet isn't installed
133 * but an AI provider is. The callback should return an integer
134 * clamped to 0–100 — values outside that range are clamped back.
135 *
136 * @since 0.19.0
137 *
138 * @param int $score Default heuristic score (0–100).
139 * @param WP_Comment $comment Comment object.
140 */
141 $score = (int) apply_filters(
142 'desktop_mode_comments_window_spam_score',
143 $score,
144 $comment
145 );
146
147 return max( 0, min( 100, $score ) );
148 }
149