PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.12
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.12
1.1.12 1.1.11 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 All 36 releases
← All changes | includes/ai-copilot/jobs.php +80 -138 0.8.9 → 1.1.12 View file →
@@ -1,189 +1,131 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — AI Copilot WP-Cron jobs.
3 + * OpenStation — AI Copilot WP-Cron jobs.
4 4 *
5 - * Registers the three async job hooks that actually call OpenAI and
6 - * store results. Jobs are scheduled by the WordPress hooks in
7 - * `hooks.php` with a short delay so they run outside the HTTP
8 - * request that triggered the save — keeping the editor snappy even
9 - * when the OpenAI API is slow.
5 + * Registers the async comment-analysis hook that scores a comment for
6 + * spam/harmful content and stores the verdict in comment meta.
10 7 *
11 - * Hook names:
12 - * desktop_mode_ai_analyze_post ($post_id, $user_id)
13 - * desktop_mode_ai_analyze_term ($term_id, $taxonomy, $user_id)
8 + * Nothing in the plugin schedules this hook: automatic comment scoring was
9 + * removed (`docs/migration-comments-ai-scoring.md`). The callback is kept
10 + * because the hook name is a frozen identifier that a site may still have
11 + * queued events for, and because an external plugin re-implementing the
12 + * feature can schedule it rather than re-deriving the prompt and schema.
13 + *
14 + * Generation routes through the WordPress AI Client (`wp_ai_client_prompt()`),
15 + * which sources credentials from Settings → Connectors — the copilot never
16 + * handles an API key.
17 + *
18 + * Hook name:
14 19 * desktop_mode_ai_analyze_comment ($comment_id, $user_id)
15 20 *
16 - * @package WPDesktopMode
21 + * @package OpenStation
17 22 */
18 23
19 24 defined( 'ABSPATH' ) || exit;
20 25
21 26 // ---------------------------------------------------------------------------
22 -// Job: post / page
27 +// Job: comment
23 28 // ---------------------------------------------------------------------------
24 29
25 30 /**
26 - * Analyzes a post or page and stores the result in post meta.
31 + * Analyzes a comment and stores the result in comment meta.
27 32 *
28 - * @since 0.14.0
29 - *
30 - * @param int $post_id The post ID.
31 - * @param int $user_id The ID of the user whose API key to use.
33 + * @param int $comment_id The comment ID.
34 + * @param int $user_id The user to attribute the request to.
32 35 */
33 -function desktop_mode_ai_job_analyze_post( $post_id, $user_id ) {
34 - $post_id = (int) $post_id;
35 - $user_id = (int) $user_id;
36 +function openstation_ai_job_analyze_comment( $comment_id, $user_id ) {
37 + $comment_id = (int) $comment_id;
38 + $user_id = (int) $user_id;
36 39
37 - if ( ! desktop_mode_ai_is_enabled( $user_id ) ) {
40 + // No usable text-generation provider is configured in Connectors — nothing
41 + // to do. Whoever queued the event may have checked already, but the job runs
42 + // async, so re-check to avoid failed requests if the provider was removed.
43 + if ( ! openstation_ai_provider_configured() ) {
38 44 return;
39 45 }
40 46
41 - $post = get_post( $post_id );
42 - if ( ! $post instanceof WP_Post ) {
47 + $comment = get_comment( $comment_id );
48 + if ( ! $comment instanceof WP_Comment ) {
43 49 return;
44 50 }
45 51
46 - /** @var array $supported_types Post types the copilot analyzes. */
47 - $supported_types = (array) apply_filters(
48 - 'desktop_mode_ai_supported_post_types',
49 - array( 'post', 'page' )
50 - );
51 - if ( ! in_array( $post->post_type, $supported_types, true ) ) {
52 - return;
53 - }
52 + $result = openstation_ai_analyze_comment_now( $comment, $user_id );
54 53
55 - $api_key = desktop_mode_ai_get_api_key( $user_id );
56 - $messages = desktop_mode_ai_messages_for_post( $post );
57 - $schema = desktop_mode_ai_schema_content();
58 -
59 - $result = desktop_mode_ai_provider_structured_request( $user_id, $api_key, $messages, $schema, 'content_analysis' );
60 -
61 54 if ( is_wp_error( $result ) ) {
62 55 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
63 - error_log( '[WP Desktop Mode AI] Post ' . $post_id . ' analysis failed: ' . $result->get_error_message() );
56 + error_log( '[WP OpenStation AI] Comment ' . $comment_id . ' analysis failed: ' . $result->get_error_message() );
64 57 return;
65 58 }
66 59
67 - desktop_mode_ai_save_meta( 'post', $post_id, $result );
60 + openstation_ai_save_meta( 'comment', $comment_id, $result );
68 61
69 62 /**
70 - * Fires after a post or page has been successfully analyzed by the AI copilot.
63 + * Fires after a comment has been successfully analyzed by the AI copilot.
71 64 *
72 - * @since 0.14.0
65 + * The `$result` array always contains `topic`, `ai_summary`, `harmful`,
66 + * and `spam`. Downstream plugins (e.g. a moderation helper) can act on
67 + * `harmful` or `spam` here rather than polling meta.
73 68 *
74 - * @param int $post_id The post ID.
75 - * @param array $result The structured analysis result.
76 - * @param WP_Post $post The post object.
69 + * @param int $comment_id The comment ID.
70 + * @param array $result The structured analysis result.
71 + * @param WP_Comment $comment The comment object.
77 72 */
78 - do_action( 'desktop_mode_ai_post_analyzed', $post_id, $result, $post );
73 + do_action( 'openstation_ai_comment_analyzed', $comment_id, $result, $comment );
79 74 }
80 -add_action( 'desktop_mode_ai_analyze_post', 'desktop_mode_ai_job_analyze_post', 10, 2 );
75 +add_action( 'desktop_mode_ai_analyze_comment', 'openstation_ai_job_analyze_comment', 10, 2 );
81 76
82 -// ---------------------------------------------------------------------------
83 -// Job: taxonomy term (category, tag, custom)
84 -// ---------------------------------------------------------------------------
85 -
86 77 /**
87 - * Analyzes a taxonomy term and stores the result in term meta.
78 + * Runs the structured comment analysis through the AI Client.
88 79 *
89 - * @since 0.14.0
80 + * Converts the chat-style prompt from {@see openstation_ai_messages_for_comment()}
81 + * into a system instruction + user prompt, requests JSON constrained to the
82 + * comment schema, and decodes the result.
90 83 *
91 - * @param int $term_id The term ID.
92 - * @param string $taxonomy The taxonomy slug.
93 - * @param int $user_id The ID of the user whose API key to use.
84 + * @param WP_Comment $comment The comment to analyze.
85 + * @param int $user_id Requesting user id.
86 + * @return array|WP_Error Structured `{ topic, ai_summary, harmful, spam }` or an error.
94 87 */
95 -function desktop_mode_ai_job_analyze_term( $term_id, $taxonomy, $user_id ) {
96 - $term_id = (int) $term_id;
97 - $user_id = (int) $user_id;
88 +function openstation_ai_analyze_comment_now( WP_Comment $comment, $user_id ) {
89 + $messages = openstation_ai_messages_for_comment( $comment );
90 + $schema = openstation_ai_schema_comment();
98 91
99 - if ( ! desktop_mode_ai_is_enabled( $user_id ) ) {
100 - return;
92 + $system = '';
93 + $prompt = '';
94 + foreach ( $messages as $message ) {
95 + $role = isset( $message['role'] ) ? $message['role'] : '';
96 + if ( 'system' === $role ) {
97 + $system = (string) $message['content'];
98 + } elseif ( 'user' === $role ) {
99 + $prompt = (string) $message['content'];
100 + }
101 101 }
102 102
103 - $term = get_term( $term_id, $taxonomy );
104 - if ( ! $term instanceof WP_Term ) {
105 - return;
103 + $builder = wp_ai_client_prompt( $prompt );
104 + if ( '' !== $system ) {
105 + $builder = $builder->using_system_instruction( $system );
106 106 }
107 + // Provider + model are chosen by the Core AI Client unless the
108 + // model-config filter says otherwise.
107 109
108 - $api_key = desktop_mode_ai_get_api_key( $user_id );
109 - $messages = desktop_mode_ai_messages_for_term( $term );
110 - $schema = desktop_mode_ai_schema_content();
110 + $builder = $builder->as_json_response( openstation_ai_normalize_response_schema( $schema ) );
111 + $builder = openstation_ai_apply_model_config(
112 + $builder,
113 + array(
114 + 'user_id' => (int) $user_id,
115 + 'source' => 'ai-copilot/comment-analysis',
116 + 'has_schema' => true,
117 + )
118 + );
111 119
112 - $result = desktop_mode_ai_provider_structured_request( $user_id, $api_key, $messages, $schema, 'content_analysis' );
113 -
114 - if ( is_wp_error( $result ) ) {
115 - // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
116 - error_log( '[WP Desktop Mode AI] Term ' . $term_id . ' (' . $taxonomy . ') analysis failed: ' . $result->get_error_message() );
117 - return;
120 + $json = $builder->generate_text();
121 + if ( is_wp_error( $json ) ) {
122 + return $json;
118 123 }
119 124
120 - desktop_mode_ai_save_meta( 'term', $term_id, $result );
121 -
122 - /**
123 - * Fires after a taxonomy term has been successfully analyzed by the AI copilot.
124 - *
125 - * @since 0.14.0
126 - *
127 - * @param int $term_id The term ID.
128 - * @param array $result The structured analysis result.
129 - * @param WP_Term $term The term object.
130 - */
131 - do_action( 'desktop_mode_ai_term_analyzed', $term_id, $result, $term );
132 -}
133 -add_action( 'desktop_mode_ai_analyze_term', 'desktop_mode_ai_job_analyze_term', 10, 3 );
134 -
135 -// ---------------------------------------------------------------------------
136 -// Job: comment
137 -// ---------------------------------------------------------------------------
138 -
139 -/**
140 - * Analyzes a comment and stores the result in comment meta.
141 - *
142 - * @since 0.14.0
143 - *
144 - * @param int $comment_id The comment ID.
145 - * @param int $user_id The ID of the user whose API key to use.
146 - */
147 -function desktop_mode_ai_job_analyze_comment( $comment_id, $user_id ) {
148 - $comment_id = (int) $comment_id;
149 - $user_id = (int) $user_id;
150 -
151 - if ( ! desktop_mode_ai_is_enabled( $user_id ) ) {
152 - return;
125 + $result = json_decode( (string) $json, true );
126 + if ( ! is_array( $result ) ) {
127 + return new WP_Error( 'openstation_ai_bad_json', 'The AI response was not valid JSON.' );
153 128 }
154 129
155 - $comment = get_comment( $comment_id );
156 - if ( ! $comment instanceof WP_Comment ) {
157 - return;
158 - }
159 -
160 - $api_key = desktop_mode_ai_get_api_key( $user_id );
161 - $messages = desktop_mode_ai_messages_for_comment( $comment );
162 - $schema = desktop_mode_ai_schema_comment();
163 -
164 - $result = desktop_mode_ai_provider_structured_request( $user_id, $api_key, $messages, $schema, 'comment_analysis' );
165 -
166 - if ( is_wp_error( $result ) ) {
167 - // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
168 - error_log( '[WP Desktop Mode AI] Comment ' . $comment_id . ' analysis failed: ' . $result->get_error_message() );
169 - return;
170 - }
171 -
172 - desktop_mode_ai_save_meta( 'comment', $comment_id, $result );
173 -
174 - /**
175 - * Fires after a comment has been successfully analyzed by the AI copilot.
176 - *
177 - * The `$result` array always contains `topic`, `ai_summary`, `harmful`,
178 - * and `spam`. Downstream plugins (e.g. a moderation helper) can act on
179 - * `harmful` or `spam` here rather than polling meta.
180 - *
181 - * @since 0.14.0
182 - *
183 - * @param int $comment_id The comment ID.
184 - * @param array $result The structured analysis result.
185 - * @param WP_Comment $comment The comment object.
186 - */
187 - do_action( 'desktop_mode_ai_comment_analyzed', $comment_id, $result, $comment );
130 + return $result;
188 131 }
189 -add_action( 'desktop_mode_ai_analyze_comment', 'desktop_mode_ai_job_analyze_comment', 10, 2 );