410 lines
14 KiB
PHP
410 lines
14 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Helper class for managing Campaigns.
|
|
*/
|
|
class RL_MailWarmer_Campaign_Helper
|
|
{
|
|
/**
|
|
* Calculate the campaign timeline with randomized daily email goals.
|
|
*
|
|
* @param int $campaign_id The campaign post ID.
|
|
* @return array Weekly email schedule with randomized daily goals.
|
|
* @throws Exception If the campaign parameters are invalid.
|
|
*/
|
|
public static function calculate_campaign_timeline($campaign_id)
|
|
{
|
|
// Fetch campaign details
|
|
$warmup_period = (int) get_post_meta($campaign_id, 'warmup_period', true); // Weeks
|
|
$target_volume = (int) get_post_meta($campaign_id, 'target_volume', true); // Daily emails to ramp up to
|
|
$start_date = get_post_meta($campaign_id, 'start_date', true); // Campaign start date
|
|
|
|
if (!$warmup_period || !$target_volume || !$start_date) {
|
|
throw new Exception(__('Invalid campaign parameters.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Fetch ACF options
|
|
$holidays_raw = get_field('calendar_holidays', 'option'); // Holidays in 12/25/25 format
|
|
$holidays = array_map(
|
|
fn($date) => DateTime::createFromFormat('m/d/y', trim($date))->format('Y-m-d'),
|
|
explode("\n", $holidays_raw)
|
|
);
|
|
|
|
$min_starting_volume = (int) get_field('min_starting_email_volume', 'option') ?: 10;
|
|
$max_daily_volume = (int) get_field('max_campaign_daily_email_volume', 'option') ?: 1000;
|
|
|
|
// Calculate starting daily volume (2.5% of target volume)
|
|
$starting_daily_volume = max(ceil($target_volume * 0.025), $min_starting_volume);
|
|
|
|
// Initialize variables
|
|
$timeline = [];
|
|
$total_days = $warmup_period * 7; // Total days in the campaign
|
|
$start_date = new DateTime($start_date);
|
|
|
|
// Calculate daily ramp-up rate
|
|
$daily_increase = ($target_volume - $starting_daily_volume) / $total_days;
|
|
|
|
// Generate timeline
|
|
for ($day = 0; $day < $total_days; $day++) {
|
|
$current_date = clone $start_date;
|
|
$current_date->modify("+{$day} days");
|
|
$date_formatted = $current_date->format('Y-m-d');
|
|
|
|
// Adjust for holidays and weekends
|
|
$is_weekend = in_array($current_date->format('N'), [6, 7]);
|
|
$is_holiday = in_array($date_formatted, $holidays);
|
|
$reduction_factor = ($is_weekend || $is_holiday) ? rand(65, 82) / 100 : 1;
|
|
|
|
// Calculate daily volume
|
|
$daily_volume = min(
|
|
ceil(($starting_daily_volume + ($daily_increase * $day)) * $reduction_factor),
|
|
$target_volume * 1.2
|
|
);
|
|
|
|
if ($daily_volume > $max_daily_volume) {
|
|
$daily_volume = $max_daily_volume;
|
|
}
|
|
|
|
$timeline[$date_formatted] = $daily_volume;
|
|
}
|
|
|
|
// Save the timeline as a JSON string to the campaign post
|
|
$timeline_json = json_encode($timeline);
|
|
// log_to_file("Timeline JSON: $timeline_json");
|
|
update_post_meta($campaign_id, 'campaign_timeline', $timeline_json);
|
|
|
|
return $timeline;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate a conversation for a campaign.
|
|
*
|
|
* @param int $campaign_id The ID of the campaign.
|
|
* @param array $conversation_steps Blueprint of the conversation (participants, replies, etc.).
|
|
* @param string $prompt AI prompt for generating email content.
|
|
*
|
|
* @return int|WP_Error The ID of the created conversation or a WP_Error on failure.
|
|
*/
|
|
public static function generate_conversation($campaign_id, $conversation_steps, $prompt) {
|
|
if (empty($campaign_id) || empty($conversation_steps)) {
|
|
return new WP_Error('invalid_data', __('Invalid campaign or conversation steps.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Insert the conversation into the database
|
|
$conversation_id = RL_MailWarmer_DB_Helper::insert_conversation($campaign_id, $conversation_steps, $prompt);
|
|
|
|
if (!$conversation_id) {
|
|
return new WP_Error('db_error', __('Failed to create conversation.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
return $conversation_id;
|
|
}
|
|
|
|
/**
|
|
* Generate messages for a conversation.
|
|
*
|
|
* @param int $conversation_id The ID of the conversation.
|
|
* @param int $campaign_id The ID of the campaign.
|
|
* @param array $steps The steps of the conversation.
|
|
*
|
|
* @return array An array of message IDs or WP_Error on failure.
|
|
*/
|
|
public static function generate_messages($conversation_id, $campaign_id, $steps) {
|
|
if (empty($conversation_id) || empty($campaign_id) || empty($steps)) {
|
|
return new WP_Error('invalid_data', __('Invalid conversation or steps.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
$message_ids = [];
|
|
|
|
foreach ($steps as $step) {
|
|
$scheduled_for = $step['scheduled_for'];
|
|
$from_email = $step['from'];
|
|
$to_email = implode(',', $step['to']);
|
|
$cc = isset($step['cc']) ? implode(',', $step['cc']) : null;
|
|
$subject = $step['subject'];
|
|
$body = $step['body'];
|
|
|
|
// Insert the message into the database
|
|
$message_id = RL_MailWarmer_DB_Helper::insert_message(
|
|
$campaign_id,
|
|
$conversation_id,
|
|
$scheduled_for,
|
|
$from_email,
|
|
$to_email,
|
|
$cc,
|
|
$subject,
|
|
$body
|
|
);
|
|
|
|
if (!$message_id) {
|
|
return new WP_Error('db_error', __('Failed to create message.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
$message_ids[] = $message_id;
|
|
}
|
|
|
|
return $message_ids;
|
|
}
|
|
|
|
/**
|
|
* Generate conversations and messages for a campaign.
|
|
*
|
|
* @param int $campaign_id The ID of the campaign.
|
|
* @param int $weekly_volume The target weekly email volume.
|
|
*
|
|
* @return array An array of created conversation IDs or WP_Error on failure.
|
|
*/
|
|
public static function generate_conversations($campaign_id, $weekly_volume) {
|
|
$conversation_ids = [];
|
|
|
|
// Example: Break down weekly volume into conversation ratios
|
|
$conversation_ratios = [
|
|
['percentage' => 5, 'participants' => [3, 6], 'replies' => [5, 12]],
|
|
['percentage' => 10, 'participants' => [3, 5], 'replies' => [3, 5]],
|
|
['percentage' => 15, 'participants' => [2, 4], 'replies' => [1, 3]],
|
|
['percentage' => 70, 'participants' => [2, 6], 'replies' => [0, 0]],
|
|
];
|
|
|
|
foreach ($conversation_ratios as $ratio) {
|
|
$volume = ceil($weekly_volume * ($ratio['percentage'] / 100));
|
|
|
|
for ($i = 0; $i < $volume; $i++) {
|
|
$conversation_steps = []; // Generate steps based on ratio
|
|
$prompt = ''; // Generate prompt for AI (to be implemented)
|
|
|
|
// Create conversation
|
|
$conversation_id = self::generate_conversation($campaign_id, $conversation_steps, $prompt);
|
|
|
|
if (is_wp_error($conversation_id)) {
|
|
return $conversation_id;
|
|
}
|
|
|
|
$steps = []; // Generate steps for this conversation
|
|
$message_result = self::generate_messages($conversation_id, $campaign_id, $steps);
|
|
|
|
if (is_wp_error($message_result)) {
|
|
return $message_result;
|
|
}
|
|
|
|
$conversation_ids[] = $conversation_id;
|
|
}
|
|
}
|
|
|
|
return $conversation_ids;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate additional email accounts for the campaign.
|
|
*
|
|
* Creates additional email accounts for initiating email conversations,
|
|
* replying to chains, and being included on CCs to increase email volume.
|
|
*
|
|
* @param int $campaign_id The campaign post ID.
|
|
* @return array List of generated email accounts.
|
|
*/
|
|
public static function generate_campaign_email_accounts($campaign_id)
|
|
{
|
|
// Implementation placeholder
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Generate conversation vectors for the campaign.
|
|
*
|
|
* Creates a detailed map of email chains, participants, and timelines,
|
|
* based on campaign requirements.
|
|
*
|
|
* @param int $campaign_id The campaign post ID.
|
|
* @return array List of conversation vectors with timestamps and participants.
|
|
*/
|
|
public static function generate_campaign_conversation_vectors($campaign_id)
|
|
{
|
|
// Implementation placeholder
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Generate email conversation content using ChatGPT.
|
|
*
|
|
* Creates realistic email conversations with context and replies,
|
|
* leveraging ChatGPT for natural language generation.
|
|
*
|
|
* @param int $campaign_id The campaign post ID.
|
|
* @param array $vector Details of the conversation vector (participants, topics, etc.).
|
|
* @return string Generated email conversation content.
|
|
*/
|
|
public static function generate_email_conversation($campaign_id, $vector)
|
|
{
|
|
// Implementation placeholder
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a meta box for generating the campaign timeline.
|
|
*/
|
|
add_action('add_meta_boxes', function () {
|
|
add_meta_box(
|
|
'generate_campaign_timeline',
|
|
__('Generate Timeline', 'rl-mailwarmer'),
|
|
'rl_mailwarmer_render_generate_timeline_box',
|
|
'campaign',
|
|
'side',
|
|
'default'
|
|
);
|
|
});
|
|
|
|
/**
|
|
* Render the "Generate Timeline" meta box.
|
|
*
|
|
* @param WP_Post $post The current post object.
|
|
*/
|
|
function rl_mailwarmer_render_generate_timeline_box($post)
|
|
{
|
|
// Add a nonce for security
|
|
wp_nonce_field('generate_timeline_nonce', 'generate_timeline_nonce_field');
|
|
|
|
// Render the form
|
|
?>
|
|
<form id="generate-timeline-form">
|
|
<p>
|
|
<button type="button" id="generate-timeline-button" class="button button-primary">
|
|
<?php esc_html_e('Generate Timeline', 'rl-mailwarmer'); ?>
|
|
</button>
|
|
</p>
|
|
<div id="generate-timeline-result"></div>
|
|
</form>
|
|
<?php
|
|
}
|
|
|
|
add_action('admin_enqueue_scripts', function ($hook) {
|
|
global $post;
|
|
|
|
// Ensure this is the Add/Edit Campaign screen
|
|
if (($hook === 'post.php' || $hook === 'post-new.php') && $post->post_type === 'campaign') {
|
|
wp_enqueue_script(
|
|
'rl-mailwarmer-generate-timeline-js',
|
|
RL_MAILWARMER_URL . 'js/generate-timeline.js', // Adjust path as needed
|
|
['jquery'],
|
|
null,
|
|
true
|
|
);
|
|
|
|
wp_localize_script('rl-mailwarmer-generate-timeline-js', 'rlMailWarmerGenerateTimeline', [
|
|
'ajax_url' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('generate_timeline_nonce'),
|
|
]);
|
|
}
|
|
});
|
|
|
|
add_action('wp_ajax_rl_mailwarmer_generate_timeline', function () {
|
|
// Verify nonce
|
|
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'generate_timeline_nonce')) {
|
|
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Validate and sanitize input
|
|
$post_id = intval($_POST['post_id']);
|
|
if (!$post_id || get_post_type($post_id) !== 'campaign') {
|
|
wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Generate the timeline
|
|
try {
|
|
$timeline = RL_MailWarmer_Campaign_Helper::calculate_campaign_timeline($post_id);
|
|
// log_to_file("wp_ajax_rl_mailwarmer_generate_timeline - Generated Timeline: ", $timeline);
|
|
// update_post_meta($post_id, "campaign_timeline", $timeline);
|
|
wp_send_json_success($timeline);
|
|
} catch (Exception $e) {
|
|
wp_send_json_error($e->getMessage());
|
|
}
|
|
});
|
|
|
|
// Cal-Heatmap
|
|
|
|
|
|
|
|
add_action('edit_form_after_title', function ($post) {
|
|
if ($post->post_type === 'campaign') {
|
|
echo '<div id="campaign-timeline-heatmap" style="width: 100%; height: 300px; border:1px solid #fff;"></div>';
|
|
}
|
|
});
|
|
add_action('admin_enqueue_scripts', function ($hook) {
|
|
global $post;
|
|
|
|
if (($hook === 'post.php' || $hook === 'post-new.php') && $post->post_type === 'campaign') {
|
|
// Enqueue D3.js (required for Cal-Heatmap)
|
|
wp_enqueue_script(
|
|
'd3-js',
|
|
'https://d3js.org/d3.v7.min.js',
|
|
[],
|
|
'7.0.0',
|
|
true
|
|
);
|
|
|
|
// Enqueue Cal-Heatmap
|
|
wp_enqueue_script(
|
|
'cal-heatmap',
|
|
'https://unpkg.com/cal-heatmap/dist/cal-heatmap.min.js',
|
|
['d3-js'],
|
|
'4.2.4',
|
|
true
|
|
);
|
|
|
|
// Enqueue Cal-Heatmap CSS
|
|
wp_enqueue_style(
|
|
'cal-heatmap',
|
|
'https://unpkg.com/cal-heatmap/dist/cal-heatmap.css',
|
|
[],
|
|
'4.2.4'
|
|
);
|
|
|
|
// Custom heatmap script
|
|
wp_enqueue_script(
|
|
'rl-mailwarmer-campaign-heatmap',
|
|
RL_MAILWARMER_URL . 'js/campaign-timeline-heatmap.js',
|
|
['cal-heatmap', 'jquery'],
|
|
null,
|
|
true
|
|
);
|
|
|
|
// Pass data to the script
|
|
wp_localize_script('rl-mailwarmer-campaign-heatmap', 'rlMailWarmerHeatmap', [
|
|
'ajax_url' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('campaign_timeline_nonce'),
|
|
'post_id' => $post->ID,
|
|
]);
|
|
}
|
|
});
|
|
|
|
add_action('wp_ajax_rl_mailwarmer_get_timeline', function () {
|
|
// Verify nonce
|
|
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'campaign_timeline_nonce')) {
|
|
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Validate and sanitize input
|
|
$post_id = intval($_POST['post_id']);
|
|
if (!$post_id || get_post_type($post_id) !== 'campaign') {
|
|
wp_send_json_error(__('Invalid campaign ID.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Retrieve the timeline
|
|
$timeline_json = get_post_meta($post_id, 'campaign_timeline', true);
|
|
$timeline = json_decode($timeline_json, true);
|
|
|
|
if (!$timeline) {
|
|
wp_send_json_error(__('No timeline data found.', 'rl-mailwarmer'));
|
|
}
|
|
|
|
// Convert timeline to Cal-Heatmap format (timestamp => value)
|
|
$heatmap_data = [];
|
|
foreach ($timeline as $date => $volume) {
|
|
$timestamp = strtotime($date); // Convert to UNIX timestamp
|
|
$heatmap_data[$timestamp] = $volume;
|
|
}
|
|
|
|
wp_send_json_success($heatmap_data);
|
|
});
|