PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.13
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.13
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / assets / php / ai-conversations.php

ai-conversations.php in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.13, at assets/php/ai-conversations.php

77 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /** Private, per-user and per-page conversations, independent of pairing credentials. */
3 if ( ! defined( 'ABSPATH' ) ) { exit; }
4 function bkbg_chat_key( $user, $post ) { return 'bkbg_ai_chats_' . get_current_blog_id() . '_' . (int) $user . '_' . (int) $post; }
5 function bkbg_chat_read( $user, $post ) {
6 $value = get_option( bkbg_chat_key( $user, $post ) );
7 return is_array( $value ) ? $value : array( 'active' => '', 'chats' => array() );
8 }
9 function bkbg_chat_change( $user, $post, $callback ) {
10 global $wpdb;
11 $key = bkbg_chat_key( $user, $post );
12 add_option( $key, array( 'active' => '', 'chats' => array() ), '', false );
13 for ( $i = 0; $i < 5; $i++ ) {
14 wp_cache_delete( $key, 'options' ); $old = get_option( $key ); $next = $old;
15 $result = $callback( $next );
16 if ( is_wp_error( $result ) ) { return $result; }
17 if ( $next === $old ) { return $next; }
18 if ( strlen( maybe_serialize( $next ) ) > 15000000 ) { return bkbg_external_error( 'Conversation storage is full.', 413 ); }
19 $changed = $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->options} SET option_value=%s WHERE option_name=%s AND BINARY option_value=BINARY %s", maybe_serialize( $next ), $key, maybe_serialize( $old ) ) );
20 wp_cache_delete( $key, 'options' );
21 if ( 1 === $changed ) { return $next; }
22 }
23 return bkbg_external_error( 'Chat changed concurrently. Retry saving.', 409 );
24 }
25 function bkbg_chat_valid_id( $id ) { return is_string( $id ) && preg_match( '/^[a-zA-Z0-9_-]{8,80}$/D', $id ); }
26 function bkbg_chat_save( $request ) {
27 $chat = $request['chat'];
28 if ( strlen( $request->get_body() ) > 2000000 || ! is_array( $chat ) || ! bkbg_chat_valid_id( $chat['id'] ?? null ) || ! is_array( $chat['messages'] ?? null ) || ! is_array( $chat['history'] ?? null ) ) { return bkbg_external_error( 'Invalid or oversized conversation.', 413 ); }
29 foreach ( $chat['messages'] as $message ) {
30 if ( ! is_array( $message ) || ! in_array( $message['role'] ?? '', array( 'user', 'assistant', 'error' ), true ) || ! is_string( $message['text'] ?? null ) ) { return bkbg_external_error( 'Invalid chat message.' ); }
31 }
32 $chat['title'] = sanitize_text_field( substr( $chat['title'] ?? 'Conversation', 0, 240 ) );
33 return bkbg_chat_change( get_current_user_id(), absint( $request['post_id'] ), function ( &$library ) use ( $chat ) {
34 $id = $chat['id']; $old = $library['chats'][ $id ] ?? array();
35 // External messages may arrive while the editor saves. Preserve those IDs.
36 $ids = array();
37 foreach ( $chat['messages'] as $index => $incoming ) { if ( isset( $incoming['id'] ) ) { $ids[ $incoming['id'] ] = $index; } }
38 foreach ( $old['messages'] ?? array() as $message ) {
39 if ( ! empty( $message['external'] ) ) {
40 $index = $ids[ $message['id'] ] ?? null;
41 if ( null === $index ) { $chat['messages'][] = $message; }
42 elseif ( 'queued' !== ( $message['status'] ?? '' ) ) { $chat['messages'][ $index ] = $message; }
43 }
44 }
45 $chat['updated'] = microtime( true ) * 1000;
46 $library['chats'][ $id ] = $chat; $library['active'] = $id;
47 } );
48 }
49 function bkbg_chat_append( $s, $message ) {
50 if ( empty( $s['chat_id'] ) ) { return true; } // Legacy sessions have no saved conversation.
51 return bkbg_chat_change( $s['user'], $s['post'], function ( &$library ) use ( $s, $message ) {
52 $id = $s['chat_id'];
53 if ( empty( $library['chats'][ $id ] ) ) { $library['chats'][ $id ] = array( 'id' => $id, 'title' => 'External conversation', 'messages' => array(), 'history' => array() ); }
54 $chat = &$library['chats'][ $id ];
55 foreach ( $chat['messages'] as &$old ) { if ( ( $old['id'] ?? '' ) === $message['id'] ) { $old = $message; $chat['updated'] = microtime( true ) * 1000; return; } } unset( $old );
56 $chat['messages'][] = $message; $chat['updated'] = microtime( true ) * 1000;
57 } );
58 }
59 function bkbg_chat_can_access( $request ) {
60 if ( ! bkbg_ai_user_can_use() ) {
61 return false;
62 }
63 $post_id = absint( $request['post_id'] );
64 return $post_id && get_post( $post_id ) && current_user_can( 'edit_post', $post_id );
65 }
66 add_action( 'rest_api_init', function () {
67 register_rest_route( 'blockenberg/v1', '/ai/conversations/(?P<post_id>\d+)', array(
68 array( 'methods' => 'GET', 'callback' => function ( $r ) { return bkbg_chat_read( get_current_user_id(), absint( $r['post_id'] ) ); }, 'permission_callback' => 'bkbg_chat_can_access' ),
69 array( 'methods' => 'POST', 'callback' => 'bkbg_chat_save', 'permission_callback' => 'bkbg_chat_can_access' ),
70 ) );
71 } );
72
73 add_filter( 'rest_post_dispatch', function ( $response, $server, $request ) {
74 if ( strpos( $request->get_route(), '/blockenberg/v1/ai/conversations/' ) === 0 && $response instanceof WP_REST_Response ) { $response->header( 'Cache-Control', 'no-store, private' ); }
75 return $response;
76 }, 10, 3 );
77