rl-warmup-plugin/includes/class-rl-mailwarmer-conversation-helper.php
2025-01-15 10:49:39 -06:00

432 lines
No EOL
18 KiB
PHP

<?php
class RL_MailWarmer_Conversation_Handler {
/**
* Process conversations starting within the next 24 hours.
*
* This function finds conversations with a status of "new" that are scheduled to start within the next 24 hours,
* generates them, and updates their status to "generated." Limits to 50 conversations per run.
*/
public static function process_upcoming_conversations() {
log_to_file("process_upcoming_conversations - Running!");
global $wpdb;
$conversation_table = $wpdb->prefix . 'rl_mailwarmer_conversations';
// $current_time = current_time('mysql');
$current_time = date('Y-m-d H:i:s', strtotime('-24 hours'));
$future_time = date('Y-m-d H:i:s', strtotime('+24 hours'));
// Fetch up to 50 conversations starting within the next 24 hours
$conversations = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $conversation_table WHERE status = %s AND first_message_timestamp BETWEEN %s AND %s LIMIT 1",
'new',
$current_time,
$future_time
),
ARRAY_A
);
// log_to_file("process_upcoming_conversations - Conversations: ", $conversations);
foreach ($conversations as $conversation) {
// log_to_file("process_upcoming_conversations - Conversation: ", $conversation);
try {
// Generate conversation content using AI
// $ai_response = self::fetch_conversation_from_ai($conversation);
$prompt = $conversation['prompt'];
// log_to_file("process_upcoming_conversations - AI prompt: ", $prompt);
// $ai_response = RL_MailWarmer_Campaign_Helper::clean_ai_response(RL_MailWarmer_Campaign_Helper::generate_ai_conversation($prompt));
// $ai_response = RL_MailWarmer_Campaign_Helper::generate_ai_conversation($prompt);
$ai_response = get_post_meta($conversation['campaign_id'], 'last_ai_response', true);
// log_to_file("process_upcoming_conversations - AI Response: ", $ai_response);
if (is_wp_error($ai_response)) {
return $ai_response; // Handle AI generation failure gracefully
}
$updated_conversation_steps = self::merge_timeline_with_ai_response($conversation['conversation_steps'], $ai_response);
// log_to_file("process_upcoming_conversations - Merged steps: ", $updated_conversation_steps);
// // Update conversation status to "generated"
$conversation_table = $wpdb->prefix . 'rl_mailwarmer_conversation';
$status = 'new';
$result = $wpdb->update(
$conversation_table,
// [],
['status' => $status, 'conversation_steps' => $updated_conversation_steps],
['id' => $conversation['id']],
['%s'],
['%s'],
['%d']
);
// log_to_file("process_upcoming_conversations - Update DB Result: ", $result);
$update_messages_db = self::update_messages_with_merged_output($conversation['id'], $updated_conversation_steps);
log_to_file("process_upcoming_conversations - Result of update_messages_with_merged_output(): ", $update_messages_db);
// Update the conversation with generated steps
// log_to_file("process_upcoming_conversations - Updating conversation_steps");
// if (RL_MailWarmer_Campaign_Helper::update_conversation_steps($conversation['id'], $updated_conversation_steps)) {
// // log_to_file("process_upcoming_conversations - Updated conversation_steps!");
// }
// Save the generated content to the conversation
// RL_MailWarmer_DB_Helper::update_conversation_steps($conversation['id'], $ai_response);
} catch (Exception $e) {
error_log("Failed to generate conversation ID {$conversation['id']}: " . $e->getMessage());
}
}
}
private static function update_messages_with_merged_output($conversation_id, $merged_output) {
log_to_file("update_messages_with_merged_output - merged_output: ", $merged_output);
global $wpdb;
$message_table = $wpdb->prefix . 'rl_mailwarmer_messages';
// $merged_output = json_decode($merged_output);
// Fetch all messages for the given conversation ID
$messages = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $message_table WHERE conversation_id = %d ORDER BY `scheduled_for_timestamp` ASC",
$conversation_id
),
ARRAY_A
);
log_to_file("update_messages_with_merged_output - Found Messages: ", $messages);
if (empty($messages)) {
throw new Exception("No messages found for conversation ID $conversation_id.");
}
// Iterate through messages and merged output
foreach ($messages as $index => $message) {
if (!isset($merged_output[$index])) {
continue; // Skip if there's no corresponding merged output
}
$merged = $merged_output[$index];
// log_to_file("update_messages_with_merged_output - merged: $merged");
// Prepare updated data
// $updated_data = [
// 'status' => $merged['status'],
// 'scheduled_for_timestamp' => $merged['scheduled_for'],
// 'from_email' => $merged['from'],
// 'to_email' => json_encode($merged['to']),
// 'cc_email' => json_encode($merged['cc']),
// 'subject' => $merged['subject'],
// 'body' => $merged['body'],
// ];
// log_to_file("update_messages_with_merged_output - Updated Data: ", $updated_data);
// Update the database
// $wpdb->update(
// $message_table,
// $updated_data,
// ['id' => $message['id']], // Where clause
// [
// '%s', // status
// '%s', // scheduled_for_timestamp
// '%s', // from_email
// '%s', // to_email
// '%s', // cc_email
// '%s', // subject
// '%s', // body
// ],
// ['%d'] // ID
// );
}
return true;
}
/**
* Fetch a conversation from ChatGPT based on conversation parameters.
*
* @param array $conversation The conversation data.
* @return string The AI-generated conversation as JSON.
* @throws Exception If the API call fails.
*/
private static function fetch_conversation_from_ai($conversation) {
// Prepare the prompt for the AI request
$prompt = [
'profession' => $conversation['profession'],
'from' => $conversation['initiated_by'],
'to_pool' => json_decode($conversation['to_pool'], true),
'subject' => $conversation['subject'],
'num_of_replies' => $conversation['num_responses'],
'num_of_participants' => $conversation['num_participants'],
'max_days' => 3,
'can_reply' => json_decode($conversation['reply_pool'], true),
'available_to_cc' => json_decode($conversation['cc_pool'], true),
'start_date' => $conversation['first_message_timestamp'],
'end_date' => date('Y-m-d H:i:s', strtotime('+3 days', strtotime($conversation['first_message_timestamp'])))
];
// Call OpenAI API
$response = RL_MailWarmer_Api_Helper::call_openai_api($prompt);
if (!$response || !isset($response['choices'][0]['text'])) {
throw new Exception('Invalid response from OpenAI API');
}
return $response['choices'][0]['text'];
}
public static function merge_timeline_with_ai_response($conversation_json, $ai_response_json) {
// Decode the JSON inputs into arrays
$conversation = json_decode($conversation_json, true);
$ai_response = json_decode($ai_response_json, true);
// Ensure both arrays have the same number of elements
$merged = [];
$count_timeline = count($conversation);
$count_ai_response = count($ai_response);
if ($count_ai_response > $count_timeline) {
// Trim the AI responses to match the timeline count
$ai_response = array_slice($ai_response, 0, $count_timeline);
} elseif ($count_ai_response < $count_timeline) {
throw new Exception('The number of AI responses is less than the timeline steps.');
}
// Merge corresponding elements
foreach ($conversation as $index => $timeline_step) {
$ai_message = $ai_response[$index];
$merged[] = array_merge($timeline_step, $ai_message);
}
// Return the merged result as JSON
return json_encode($merged, JSON_PRETTY_PRINT);
}
}
add_action('admin_menu', function () {
add_menu_page(
__('Conversations', 'rl-mailwarmer'), // Page title
__('Conversations', 'rl-mailwarmer'), // Menu title
'manage_options', // Capability
'rl-mailwarmer-conversations', // Menu slug
'rl_mailwarmer_render_conversations_page', // Callback function
'dashicons-email', // Icon
8 // Position
);
});
/**
* Render the Conversations admin page with checkboxes and a Delete button.
*/
function rl_mailwarmer_render_conversations_page() {
if (!current_user_can('manage_options')) {
return;
}
global $wpdb;
$table_name = $wpdb->prefix . 'rl_mailwarmer_conversations'; // Conversation table
$conversations = $wpdb->get_results("SELECT * FROM $table_name ORDER BY created_at DESC");
?>
<div class="wrap">
<h1><?php esc_html_e('Conversations', 'rl-mailwarmer'); ?></h1>
<?php if (empty($conversations)) : ?>
<p><?php esc_html_e('No conversations found.', 'rl-mailwarmer'); ?></p>
<?php else : ?>
<form id="conversation-management-form" method="post">
<table class="widefat fixed striped">
<thead>
<tr>
<th style="width: 50px;">
<input type="checkbox" id="check-all-conversations">
</th>
<th><?php esc_html_e('ID', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Campaign ID', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Created At', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Steps', 'rl-mailwarmer'); ?></th>
<th><?php esc_html_e('Prompt', 'rl-mailwarmer'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($conversations as $conversation) : ?>
<tr>
<td>
<input type="checkbox" name="selected_conversations[]" value="<?php echo esc_attr($conversation->id); ?>">
</td>
<td><?php echo esc_html($conversation->id); ?></td>
<td><?php echo esc_html($conversation->campaign_id); ?></td>
<td><?php echo esc_html($conversation->created_at); ?></td>
<td><?php echo esc_html($conversation->conversation_steps); ?></td>
<td>
<details>
<summary><?php esc_html_e('View Prompt', 'rl-mailwarmer'); ?></summary>
<pre><?php echo esc_html($conversation->prompt); ?></pre>
</details>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p>
<button type="button" id="delete-selected-conversations" class="button button-primary">
<?php esc_html_e('Delete Items', 'rl-mailwarmer'); ?>
</button>
</p>
</form>
<?php endif; ?>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Select all conversations
const checkAll = document.getElementById('check-all-conversations');
const checkboxes = document.querySelectorAll('input[name="selected_conversations[]"]');
checkAll.addEventListener('change', function () {
checkboxes.forEach(checkbox => checkbox.checked = checkAll.checked);
});
// Handle delete button click
document.getElementById('delete-selected-conversations').addEventListener('click', function () {
const selectedIds = Array.from(checkboxes)
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.value);
if (selectedIds.length === 0) {
alert('<?php esc_html_e('Please select at least one conversation to delete.', 'rl-mailwarmer'); ?>');
return;
}
if (confirm('<?php esc_html_e('Are you sure you want to delete the selected conversations?', 'rl-mailwarmer'); ?>')) {
jQuery.post(ajaxurl, {
action: 'rl_delete_conversations',
conversation_ids: selectedIds,
}, function (response) {
if (response.success) {
alert('<?php esc_html_e('Selected conversations deleted successfully.', 'rl-mailwarmer'); ?>');
location.reload(); // Reload the page to update the list
} else {
alert('<?php esc_html_e('Failed to delete conversations.', 'rl-mailwarmer'); ?>');
}
});
}
});
});
</script>
<?php
}
function rl_mailwarmer_get_conversations($page, $per_page = 20) {
global $wpdb;
$offset = ($page - 1) * $per_page;
$total = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}rl_mailwarmer_conversation");
$conversations = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}rl_mailwarmer_conversation ORDER BY created_at DESC LIMIT %d OFFSET %d",
$per_page,
$offset
));
return ['conversations' => $conversations, 'total' => $total];
}
add_action('wp_ajax_rl_delete_conversations', function () {
global $wpdb;
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => __('Permission denied.', 'rl-mailwarmer')]);
}
$conversation_ids = isset($_POST['conversation_ids']) ? array_map('intval', $_POST['conversation_ids']) : [];
if (empty($conversation_ids)) {
wp_send_json_error(['message' => __('No conversations selected.', 'rl-mailwarmer')]);
}
$table_name = $wpdb->prefix . 'rl_mailwarmer_conversation';
// Delete selected conversations
$placeholders = implode(',', array_fill(0, count($conversation_ids), '%d'));
$query = "DELETE FROM $table_name WHERE id IN ($placeholders)";
$result = $wpdb->query($wpdb->prepare($query, $conversation_ids));
if ($result === false) {
wp_send_json_error(['message' => __('Failed to delete conversations.', 'rl-mailwarmer')]);
}
wp_send_json_success(['message' => __('Conversations deleted successfully.', 'rl-mailwarmer')]);
});
// Add the metabox
add_action('add_meta_boxes', 'rl_mailwarmer_add_process_conversations_metabox');
function rl_mailwarmer_add_process_conversations_metabox() {
add_meta_box(
'rl-process-conversations',
__('Process Upcoming Conversations', 'rl-mailwarmer'),
'rl_mailwarmer_process_conversations_metabox_callback',
'campaign',
'side',
'default'
);
}
// Metabox callback function
function rl_mailwarmer_process_conversations_metabox_callback($post) {
// Nonce field for security
wp_nonce_field('rl_process_conversations_nonce', 'rl_process_conversations_nonce_field');
?>
<button type="button" id="rl_process_conversations_button" class="button button-primary">
<?php _e('Process Upcoming Conversations', 'rl-mailwarmer'); ?>
</button>
<div id="rl_process_conversations_result" style="margin-top: 10px;"></div>
<?php
}
// Enqueue JavaScript
add_action('admin_enqueue_scripts', 'rl_mailwarmer_enqueue_process_conversations_script');
function rl_mailwarmer_enqueue_process_conversations_script($hook) {
if ($hook === 'post.php' || $hook === 'post-new.php') {
wp_enqueue_script(
'rl-process-conversations',
RL_MAILWARMER_URL . 'js/rl-process-conversations.js',
['jquery'],
'1.0',
true
);
wp_localize_script('rl-process-conversations', 'rlProcessConversations', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('rl_process_conversations_nonce'),
]);
}
}
// AJAX handler
add_action('wp_ajax_rl_process_upcoming_conversations', 'rl_mailwarmer_process_upcoming_conversations_handler');
function rl_mailwarmer_process_upcoming_conversations_handler() {
check_ajax_referer('rl_process_conversations_nonce', 'security');
$post_id = intval($_POST['post_id']);
if (!$post_id) {
wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer'));
}
try {
// Call the function to process upcoming conversations
RL_MailWarmer_Conversation_Handler::process_upcoming_conversations($post_id);
wp_send_json_success(__('Conversations processed successfully.', 'rl-mailwarmer'));
} catch (Exception $e) {
wp_send_json_error(__('Error processing conversations: ', 'rl-mailwarmer') . $e->getMessage());
}
}