PluginProbe
Extendify / 3.0.4
Extendify v3.0.4
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / app / Agent / Controllers / ChatHistoryController.php

ChatHistoryController.php in Extendify 3.0.4, at app/Agent/Controllers/ChatHistoryController.php

198 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Store info about messages/events from chat
5 */
6
7 namespace Extendify\Agent\Controllers;
8
9 defined('ABSPATH') || die('No direct access.');
10
11 use Extendify\Shared\Services\Sanitizer;
12
13 /**
14 * The controller
15 */
16
17 class ChatHistoryController
18 {
19 /**
20 * Initialize the controller and set up the database table.
21 *
22 * @return \WP_REST_Response
23 */
24 public static function init()
25 {
26 self::setupChatHistoryTable();
27 }
28
29 /**
30 * Get the last 150 messages
31 *
32 * @return array
33 */
34 public static function getChatHistory($user_id = null)
35 {
36 global $wpdb;
37 $table = $wpdb->prefix . 'extendify_agent_events';
38 $user_id = $user_id ?: get_current_user_id();
39
40 $results = $wpdb->get_results(
41 $wpdb->prepare(
42 "SELECT * FROM $table WHERE user_id = %d ORDER BY id DESC LIMIT 150",
43 $user_id
44 ),
45 ARRAY_A
46 );
47 return array_map(function ($item) {
48 return [
49 'id' => $item['event_id'],
50 'type' => $item['type'],
51 'details' => json_decode($item['details'], true),
52 ];
53 }, $results);
54 }
55
56
57 /**
58 * Return the data
59 *
60 * @return \WP_REST_Response
61 */
62 public static function get()
63 {
64 $messages = self::getChatHistory();
65 $state = ['state' => ['messages' => Sanitizer::sanitizeArray($messages)]];
66 return new \WP_REST_Response($state);
67 }
68
69 /**
70 * Persist the data
71 *
72 * @param \WP_REST_Request $request - The request.
73 * @return \WP_REST_Response
74 */
75 public static function store($request)
76 {
77 global $wpdb;
78 $table = $wpdb->prefix . 'extendify_agent_events';
79 $user_id = get_current_user_id();
80
81 $state = $request->get_param('state');
82 $parsed = is_string($state) ? json_decode($state, true) : $state;
83 $messages = $parsed['state']['messages'] ?? [];
84
85 // Find the latest event and only add new messages since then.
86 /* TODO: Maybe we may need a check like:
87 "if this message is assistant and so is the last, delete the last one" to avoid duplicates on retries."
88 */
89 $latest = $wpdb->get_var(
90 $wpdb->prepare(
91 "SELECT event_id FROM $table WHERE user_id = %d ORDER BY created_at DESC LIMIT 1",
92 $user_id
93 )
94 );
95 $startIndex = 0;
96 if ($latest) {
97 foreach ($messages as $i => $msg) {
98 if ($msg['id'] === $latest) {
99 $startIndex = $i + 1;
100 break;
101 }
102 }
103 }
104
105 $toInsert = array_slice($messages, $startIndex);
106 foreach ($toInsert as $msg) {
107 self::upsertEvent($msg, $user_id);
108 }
109
110 return self::get();
111 }
112
113 /**
114 * Upsert an event into the database.
115 *
116 * @param array $msg The message data.
117 * @param int $user_id The user ID.
118 * @return void
119 */
120 private static function upsertEvent($msg, $user_id)
121 {
122 global $wpdb;
123 $table = $wpdb->prefix . 'extendify_agent_events';
124 $created_at = current_time('mysql');
125 $event_id = Sanitizer::sanitizeText($msg['id']);
126 $type = Sanitizer::sanitizeText($msg['type']);
127 $details = wp_json_encode(Sanitizer::sanitizeArray($msg['details']));
128
129 $sql = $wpdb->prepare(
130 "INSERT INTO $table (created_at, event_id, type, details, user_id)
131 VALUES (%s, %s, %s, %s, %d)
132 ON DUPLICATE KEY UPDATE
133 created_at = VALUES(created_at),
134 type = VALUES(type),
135 details = VALUES(details)",
136 $created_at,
137 $event_id,
138 $type,
139 $details,
140 $user_id
141 );
142 $wpdb->query($sql);
143 }
144
145 /**
146 * Ensures the custom table exists and is up to date for MySQL.
147 * Creates the table if missing, adds missing columns, and ensures an index on user_id.
148 *
149 * @return void
150 */
151 private static function setupChatHistoryTable()
152 {
153 global $wpdb;
154 $table = $wpdb->prefix . 'extendify_agent_events';
155 $columns = [
156 'id' => "BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY",
157 'created_at' => "DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP",
158 'event_id' => "VARCHAR(64) NOT NULL",
159 'type' => "VARCHAR(64) NOT NULL",
160 'details' => "LONGTEXT",
161 'user_id' => "BIGINT UNSIGNED NOT NULL"
162 ];
163
164 // Check if the table exists and create it if not
165 $exists = $wpdb->get_var("SHOW TABLES LIKE '$table'");
166 if (!$exists) {
167 $cols = [];
168 foreach ($columns as $name => $type) {
169 $cols[] = "$name $type";
170 }
171 $sql = "CREATE TABLE $table (" . implode(',', $cols) . ", INDEX(user_id))";
172 $sql .= " " . $wpdb->get_charset_collate() . ";";
173 $wpdb->query($sql);
174 return;
175 }
176
177 // Check existing columns and add missing ones
178 $existingCols = array_column($wpdb->get_results("SHOW COLUMNS FROM $table", ARRAY_A), 'Field');
179 foreach ($columns as $name => $type) {
180 if (!in_array($name, $existingCols, true)) {
181 $wpdb->query("ALTER TABLE $table ADD COLUMN $name $type");
182 }
183 }
184
185 // Unique index on (event_id, user_id)
186 $uniqueIndex = $wpdb->get_results("SHOW INDEX FROM $table WHERE Key_name = 'unique_event_id'", ARRAY_A);
187 if (empty($uniqueIndex)) {
188 $wpdb->query("CREATE UNIQUE INDEX unique_event_id ON $table(event_id, user_id)");
189 }
190
191 // Regular index on user_id
192 $userIndex = $wpdb->get_results("SHOW INDEX FROM $table WHERE Key_name = 'user_id'", ARRAY_A);
193 if (empty($userIndex)) {
194 $wpdb->query("CREATE INDEX user_id ON $table(user_id)");
195 }
196 }
197 }
198