OXIESEC PANEL
- Current Dir:
/
/
var
/
www
/
cream
/
weeklyEmail
Server IP: 139.59.38.164
Upload:
Create Dir:
Name
Size
Modified
Perms
📁
..
-
06/17/2025 10:17:24 AM
rwxrwxr-x
📄
1.php
2.62 KB
06/12/2025 02:54:56 PM
rw-r--r--
📄
2.php
2.73 KB
06/12/2025 02:48:34 PM
rw-r--r--
📁
PHPMailer
-
05/19/2025 10:07:13 AM
rwxr-xr-x
📄
TEST.php
31.55 KB
06/16/2025 11:44:55 AM
rw-r--r--
📁
email_logs
-
06/18/2025 12:44:25 AM
rwxrwxrwx
📄
unsubscribe.php
7.32 KB
05/19/2025 10:07:13 AM
rw-r--r--
📄
weekly_email_cron_wrapper.sh
2.27 KB
05/12/2025 05:22:24 AM
rwxr-xr-x
📄
weeklyemail.php
30.67 KB
06/17/2025 04:15:00 AM
rw-r--r--
Editing: TEST.php
Close
<?php // Weekly email script for Knobly Cream App - Optimized for 900+ users // This script sends personalized weekly emails to all users with: // 1. Top post of the week (most likes) // 2. User's weekly activity summary // 3. Motivational content to encourage engagement // Database connection configuration use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\Exception; require dirname(__FILE__) . '/PHPMailer/Exception.php'; require dirname(__FILE__) . '/PHPMailer/PHPMailer.php'; require dirname(__FILE__) . '/PHPMailer/SMTP.php'; function sendEmail($toName, $toEmail, $toEmailCC, $emailSubject, $emailBody) { $mail = new PHPMailer(true); try { $mail->isSMTP(); // $mail->setFrom('donotreply@knoblycream.com', 'Knobly Cream'); // $mail->Username = 'AKIARWSGL3TOGXCYQJVY'; // $mail->Password = 'BIj9DvNM3uX+cckSX4So50fqln6DEhie6dMJpe3AjzqK'; // $mail->Host = 'email-smtp.ap-south-1.amazonaws.com'; // $mail->setFrom('cream@knobly.com', 'Knobly Cream'); // $mail->Username = '16486ca9-f4d0-4d09-9739-b48a16a8586e'; // $mail->Password = '16486ca9-f4d0-4d09-9739-b48a16a8586e'; // $mail->Host = 'smtp.postmarkapp.com'; // $mail->addCustomHeader('X-PM-Message-Stream', 'outbound'); $mail->setFrom('donotreply@knobly.com', 'Knobly Cream'); $mail->Host = 'smtp.gmail.com'; $mail->Username = 'donotreply@knobly.com'; $mail->Password = 'ipmhstdrbkwfhcna'; $mail->Port = 587; $mail->SMTPAuth = true; $mail->SMTPSecure = 'tls'; if ($toEmail != '') { $arrEmail = explode(',', $toEmail); foreach ($arrEmail as $value) { $mail->addAddress(trim($value)); } } //$mail->addAddress($toEmail, $toName); if ($toEmailCC != '') { $arrCC = explode(',', $toEmailCC); foreach ($arrCC as $value) { $mail->addCC(trim($value)); } } $mail->isHTML(true); $mail->Subject = $emailSubject; $mail->Body = $emailBody; $mail->send(); return ['status' => 'success', 'message' => 'Mail sent successfully']; } catch (Exception $e) { return ['status' => 'error', 'message' => $mail->ErrorInfo]; } } function simpleEncDec($string, $action = 'e') { $secret_key = 'knoblyCream@2020'; $secret_iv = 'my_simple_secret_iv'; $output = false; $encrypt_method = "AES-256-CBC"; $key = hash('sha256', $secret_key); $iv = substr(hash('sha256', $secret_iv), 0, 16); if ($action == 'e') { $output = base64_encode(openssl_encrypt($string, $encrypt_method, $key, 0, $iv)); } else if ($action == 'd') { $output = openssl_decrypt(base64_decode($string), $encrypt_method, $key, 0, $iv); } return $output; } $host = '139.59.38.164'; $username = 'root'; $password = 'newstart'; $database = 'cream'; $creamdb = new mysqli($host, $username, $password, $database); if ($creamdb->connect_error) { die("Connection failed: " . $mysqli->connect_error); } $creamdb->set_charset('utf8mb4'); // Log file setup $logFile = "email_logs/weekly_email_" . date('Y-m-d') . ".log"; $logDir = dirname($logFile); if (!is_dir($logDir)) { mkdir($logDir, 0755, true); } // Email configuration $emailConfig = [ 'subject' => 'Your Weekly Knobly Cream Update', 'batch_size' => 50, // Process users in batches of 50 'sleep_between_emails' => 100000, // 100ms pause between emails (microseconds) 'max_execution_time' => 0 // No time limit (adjust based on server) ]; // Set execution time ini_set('max_execution_time', $emailConfig['max_execution_time']); // Function to log messages function logMessage($message) { global $logFile; file_put_contents($logFile, date('[Y-m-d H:i:s] ') . $message . PHP_EOL, FILE_APPEND); echo $message . PHP_EOL; } // Start logging logMessage("Starting weekly email sending process"); $startTime = microtime(true); // Check for previous progress $progressFile = "email_logs/progress.json"; $lastProcessedId = 0; if (file_exists($progressFile)) { $progress = json_decode(file_get_contents($progressFile), true); if (isset($progress['last_processed_id'])) { $lastProcessedId = $progress['last_processed_id']; logMessage("Resuming from user ID: $lastProcessedId"); } } try { // Fetch the top post of the week only once logMessage("Fetching top post of the week"); $topPost = getTopPostOfWeek($creamdb); if (!$topPost) { logMessage("Warning: No top post found for this week"); } else { logMessage("Top post found with ID: " . $topPost['streamId']); } // Get total number of active users $countQuery = "SELECT COUNT(*) as total FROM cream.user WHERE is_activated = 1 and email_notifications = 1 "; $countResult = $creamdb->query($countQuery); $totalUsers = $countResult->fetch_assoc()['total']; logMessage("Total active users: $totalUsers"); // Process users in batches $totalProcessed = 0; $totalSuccess = 0; $totalFailed = 0; do { // Get next batch of users $userQuery = "SELECT id, full_name, email FROM cream.user WHERE is_activated = 1 AND email_notifications = 1 AND id = 418 ORDER BY id ASC LIMIT " . $emailConfig['batch_size']; $userResult = $creamdb->query($userQuery); if ($userResult->num_rows == 0) { logMessage("No more users to process"); break; } logMessage("Processing batch of " . $userResult->num_rows . " users"); // Pre-fetch user activities for this batch $userIds = []; $users = []; while ($user = $userResult->fetch_assoc()) { $userIds[] = $user['id']; $users[$user['id']] = $user; } if (empty($userIds)) { break; } // Get activities for all users in this batch in a single query $activities = getUserActivitiesBatch($creamdb, $userIds); // Process each user in this batch foreach ($users as $userId => $user) { $userActivity = $activities[$userId] ?? [ 'number_of_posts' => 0, 'number_of_articles' => 0, 'likes_received' => 0, 'views_received' => 0 ]; // Generate personalized email content $emailContent = generateEmailContent($user, $userActivity, $topPost); // Generate unsubscribe token $unsubscribeToken = simpleEncDec($user['email'], 'e'); // Update email content with unsubscribe token $emailContent = str_replace('{{UNSUBSCRIBE_TOKEN}}', $unsubscribeToken, $emailContent); try { // Send email using your custom function $emailResult = sendEmail($user['full_name'], $user['email'], '', $emailConfig['subject'], $emailContent); // Log email status if ($emailResult['status'] == 'success') { $totalSuccess++; logMessage("Email sent successfully to: {$user['email']} (ID: {$user['id']})"); } else { $totalFailed++; logMessage("Failed to send email to: {$user['email']} (ID: {$user['id']}). Error: {$emailResult['message']}"); } } catch (Exception $e) { $totalFailed++; logMessage("Exception sending email to {$user['email']} (ID: {$user['id']}): " . $e->getMessage()); } // Update last processed ID $lastProcessedId = $user['id']; // Save progress file_put_contents($progressFile, json_encode([ 'last_processed_id' => $lastProcessedId, 'total_processed' => $totalProcessed + 1, 'total_success' => $totalSuccess, 'total_failed' => $totalFailed, 'last_updated' => date('Y-m-d H:i:s') ])); $totalProcessed++; // Sleep briefly to prevent overwhelming the mail server usleep($emailConfig['sleep_between_emails']); break; } logMessage("Batch complete. Processed so far: $totalProcessed"); break; } while (true); // Calculate statistics $executionTime = round(microtime(true) - $startTime, 2); logMessage("Email sending process completed"); logMessage("Total processed: $totalProcessed"); logMessage("Successfully sent: $totalSuccess"); logMessage("Failed: $totalFailed"); logMessage("Execution time: $executionTime seconds"); // Clear progress file if all users were processed successfully if ($totalProcessed == $totalUsers) { unlink($progressFile); logMessage("All users processed successfully, progress file removed"); } } catch (Exception $e) { logMessage("Critical error: " . $e->getMessage()); } finally { $creamdb->close(); } /** * Get top post of the week * * @param mysqli $db Database connection * @return array|null Top post data or null if not found */ function getTopPostOfWeek($db) { $topPostQuery = " SELECT rs.id AS streamId, rs.chat, rs.metadata, rs.mediaPath, rs.userId, u.full_name AS author_name, COUNT(DISTINCT rsl.userId) AS like_count, COUNT(DISTINCT sa.userId) AS view_count FROM reader.reader_stream rs LEFT JOIN reader.reader_stream_like rsl ON rs.id = rsl.streamId LEFT JOIN reader.stream_analytics sa ON rs.id = sa.streamId LEFT JOIN cream.user u ON rs.userId = u.id WHERE rs.postedOn >= CURDATE() - INTERVAL (DAYOFWEEK(CURDATE()) + 6) DAY AND rs.postedOn < CURDATE() - INTERVAL (DAYOFWEEK(CURDATE()) - 1) DAY AND rs.deleteFlag = 0 AND rs.visibility = 'public' GROUP BY rs.id, rs.userId, rs.chat, rs.metadata, rs.mediaPath, u.full_name ORDER BY like_count DESC, view_count DESC LIMIT 1"; $topPostResult = $db->query($topPostQuery); if ($topPostResult && $topPostResult->num_rows > 0) { return $topPostResult->fetch_assoc(); } return null; } /** * Get activities for a batch of users * * @param mysqli $db Database connection * @param array $userIds Array of user IDs * @return array Associative array of user activities indexed by user ID */ function getUserActivitiesBatch($db, $userIds) { if (empty($userIds)) { return []; } // Convert array to comma-separated string for IN clause $userIdsStr = implode(',', $userIds); // Query to get all activities for the batch of users $batchQuery = " SELECT u.id, COALESCE(posts.number_of_posts, 0) AS number_of_posts, COALESCE(articles.number_of_articles, 0) AS number_of_articles, COALESCE(likes_received.count, 0) AS likes_received, COALESCE(views_received.count, 0) AS views_received FROM cream.user u LEFT JOIN ( SELECT r.userId, COUNT(*) AS number_of_posts FROM reader.reader_stream r WHERE r.postedOn >= CURDATE() - INTERVAL 7 DAY AND r.deleteFlag = 0 AND r.userId IN ($userIdsStr) GROUP BY r.userId ) posts ON u.id = posts.userId LEFT JOIN ( SELECT uc.user_id, COUNT(*) AS number_of_articles FROM cream.user_collection uc WHERE uc.date_added >= CURDATE() - INTERVAL 7 DAY AND uc.user_id IN ($userIdsStr) GROUP BY uc.user_id ) articles ON u.id = articles.user_id LEFT JOIN ( SELECT rs.userId, COUNT(*) AS count FROM reader.reader_stream rs JOIN reader.reader_stream_like rsl ON rs.id = rsl.streamId WHERE rs.postedOn >= CURDATE() - INTERVAL 7 DAY AND rs.deleteFlag = 0 AND rs.userId IN ($userIdsStr) GROUP BY rs.userId ) likes_received ON u.id = likes_received.userId LEFT JOIN ( SELECT rs.userId, COUNT(*) AS count FROM reader.reader_stream rs JOIN reader.stream_analytics sa ON rs.id = sa.streamId WHERE rs.postedOn >= CURDATE() - INTERVAL 7 DAY AND rs.deleteFlag = 0 AND rs.userId IN ($userIdsStr) GROUP BY rs.userId ) views_received ON u.id = views_received.userId WHERE u.id IN ($userIdsStr)"; $result = $db->query($batchQuery); $activities = []; while ($row = $result->fetch_assoc()) { $activities[$row['id']] = [ 'number_of_posts' => $row['number_of_posts'], 'number_of_articles' => $row['number_of_articles'], 'likes_received' => $row['likes_received'], 'views_received' => $row['views_received'] ]; } return $activities; } /** * Generate personalized email content * * @param array $user User information * @param array $activity User's weekly activity * @param array $topPost Top post of the week * @return string HTML email content */ function generateEmailContent($user, $activity, $topPost) { // Start building email HTML $html = ' <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Weekly Knobly Cream Update</title> <style> /* Reset and base styles */ * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; line-height: 1.6; color: #2c3e50; background-color: #f8fafc; padding: 20px 10px; } .email-container { max-width: 680px; margin: 0 auto; background-color: #ffffff; border-radius: 12px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1); overflow: hidden; } /* Header with gradient */ .header { background: linear-gradient(135deg, #db5919 0%, #ff6b35 100%); color: white; padding: 40px 30px; text-align: center; position: relative; } .header::before { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: url("data:image/svg+xml,%3Csvg width=\'60\' height=\'60\' viewBox=\'0 0 60 60\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cg fill=\'none\' fill-rule=\'evenodd\'%3E%3Cg fill=\'%23ffffff\' fill-opacity=\'0.05\'%3E%3Ccircle cx=\'30\' cy=\'30\' r=\'4\'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); opacity: 0.1; } .header h1 { font-size: 28px; margin-bottom: 8px; font-weight: 700; position: relative; z-index: 1; } .header p { font-size: 16px; opacity: 0.9; position: relative; z-index: 1; } /* Content area */ .content { padding: 40px 30px; } .greeting { font-size: 18px; margin-bottom: 30px; color: #34495e; } .section { margin-bottom: 40px; } .section h2 { font-size: 24px; margin-bottom: 20px; color: #2c3e50; border-left: 4px solid #db5919; padding-left: 15px; font-weight: 600; } /* Top post styling */ .top-post { background: linear-gradient(135deg, #fff 0%, #f8fafc 100%); padding: 25px; border-radius: 12px; border: 1px solid #e2e8f0; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); transition: transform 0.2s ease; } .top-post:hover { transform: translateY(-2px); } .post-author { display: flex; align-items: center; margin-bottom: 15px; font-size: 14px; color: #64748b; } .author-avatar { width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, #db5919, #ff6b35); margin-right: 10px; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; font-size: 12px; } .post-title { font-size: 20px; font-weight: 600; margin-bottom: 12px; color: #1e293b; } .post-content { font-size: 15px; line-height: 1.7; color: #475569; margin-bottom: 20px; } .post-image { width: 100%; max-height: 300px; object-fit: cover; border-radius: 8px; margin: 15px 0; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); } .congratulations { background: linear-gradient(135deg, #10b981, #059669); color: white; padding: 15px 20px; border-radius: 8px; text-align: center; margin: 15px 0; font-weight: 600; box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3); } /* Stats grid */ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 15px; margin: 25px 0; } .stat-box { background: linear-gradient(135deg, #fff 0%, #f8fafc 100%); padding: 20px 15px; border-radius: 12px; text-align: center; border: 1px solid #e2e8f0; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05); transition: all 0.3s ease; } .stat-box:hover { transform: translateY(-3px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); } .stat-number { font-size: 32px; font-weight: 700; background: linear-gradient(135deg, #db5919, #ff6b35); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 5px; display: block; } .stat-label { font-size: 13px; color: #64748b; font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px; } /* CTA Buttons */ .cta-button { display: inline-block; background: linear-gradient(135deg, #db5919, #ff6b35); color: white; text-decoration: none; padding: 15px 30px; border-radius: 8px; text-align: center; margin: 20px auto; font-weight: 600; font-size: 16px; box-shadow: 0 4px 20px rgba(219, 89, 25, 0.3); transition: all 0.3s ease; border: none; cursor: pointer; } .cta-button:hover { transform: translateY(-2px); box-shadow: 0 6px 25px rgba(219, 89, 25, 0.4); } .cta-primary { display: block; width: fit-content; margin: 25px auto; } /* Motivational section */ .motivational { background: linear-gradient(135deg, #f0f9ff, #e0f2fe); padding: 25px; border-radius: 12px; border-left: 4px solid #0ea5e9; margin: 25px 0; } .motivational p { color: #0369a1; font-style: italic; font-size: 16px; margin: 0; } /* Footer */ .footer { background-color: #f8fafc; padding: 30px; text-align: center; border-top: 1px solid #e2e8f0; margin-top: 40px; } .footer p { font-size: 13px; color: #64748b; margin-bottom: 8px; } .footer a { color: #db5919; text-decoration: none; font-weight: 500; } .footer a:hover { text-decoration: underline; } /* Responsive design */ @media (max-width: 480px) { body { padding: 10px 5px; } .content { padding: 25px 20px; } .header { padding: 30px 20px; } .header h1 { font-size: 24px; } .stats { grid-template-columns: repeat(2, 1fr); gap: 10px; } .stat-box { padding: 15px 10px; } .stat-number { font-size: 28px; } .top-post { padding: 20px 15px; } .section h2 { font-size: 20px; } } /* Dark mode support */ @media (prefers-color-scheme: dark) { .email-container { background-color: #1e293b; } .content { color: #e2e8f0; } .greeting { color: #cbd5e1; } .section h2 { color: #f1f5f9; } .top-post { background: linear-gradient(135deg, #334155 0%, #475569 100%); border-color: #475569; } .post-content { color: #cbd5e1; } .stat-box { background: linear-gradient(135deg, #334155 0%, #475569 100%); border-color: #475569; } } </style> </head> <body> <div class="email-container"> <div class="header"> <h1>Weekly Knobly Cream Update</h1> <p>Your personalized community digest</p> </div> <div class="content"> <p class="greeting">Hello ' . htmlspecialchars($user['full_name']) . ',</p> <p style="margin-bottom: 30px; color: #64748b;">Here\'s what happened in your Knobly Cream community this week!</p>'; // Add top post of the week section if ($topPost) { // Extract title from metadata if available $postTitle = ''; if (!empty($topPost['metadata'])) { $metadata = json_decode($topPost['metadata'], true); if (isset($metadata['title'])) { $postTitle = $metadata['title']; } } // Format the post content (chat) $postContent = !empty($topPost['chat']) ? $topPost['chat'] : ''; // Check if there's media $hasMedia = !empty($topPost['mediaPath']); $html .= ' <div class="section"> <h2>Top Post of the Week</h2> <div class="top-post">'; // Author info with avatar $authorInitial = strtoupper(substr($topPost['author_name'], 0, 1)); $html .= ' <div class="post-author"> <span>By ' . htmlspecialchars($topPost['author_name']) . '</span> </div>'; // Show title if available if (!empty($postTitle)) { $html .= '<h3 class="post-title">' . htmlspecialchars($postTitle) . '</h3>'; } $html .= '<div class="post-content">' . substr(htmlspecialchars($postContent), 0, 200) . '...</div>'; // Display media if available if ($hasMedia) { $mediaPaths = explode(',', $topPost['mediaPath']); $firstMedia = trim($mediaPaths[0]); $extension = strtolower(pathinfo($firstMedia, PATHINFO_EXTENSION)); $imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp']; if (in_array($extension, $imageExtensions)) { $html .= '<img src="https://knoblycream.com/' . $firstMedia . '" class="post-image" alt="Post image" />'; } else { $html .= '<div style="justify-content: center; display: flex;"><a href="https://knoblycream.com/post-details.php?id=' . $topPost['streamId'] . '" style="display:block; text-align:center;"> <p style="color:#db5919; font-weight:bold; margin-top:8px;">▶ Watch Video</p></a></div>'; } } // Add congratulations if this is the user's own post if ($topPost['userId'] == $user['id']) { $html .= '<div class="congratulations"> Congratulations! Your post is the top post this week! </div>'; } $html .= ' <a href="https://knoblycream.com/post-details.php?id=' . $topPost['streamId'] . '" class="cta-button">View Full Post</a> </div> </div>'; } // Add activity section $html .= ' <div class="section"> <h2>Your Week\'s Activity</h2> <div class="stats"> <div class="stat-box"> <div class="stat-number">' . $activity['number_of_posts'] . '</div> <div class="stat-label">Posts Created</div> </div> <div class="stat-box"> <div class="stat-number">' . $activity['number_of_articles'] . '</div> <div class="stat-label">Articles Collected</div> </div> <div class="stat-box"> <div class="stat-number">' . $activity['likes_received'] . '</div> <div class="stat-label">Likes Received</div> </div> <div class="stat-box"> <div class="stat-number">' . $activity['views_received'] . '</div> <div class="stat-label">Views Received</div> </div> </div>'; // Add motivational message $html .= '<div class="motivational">'; $html .= generateMotivationalMessage($activity); $html .= '</div>'; $html .= '</div>'; // Call to action section $html .= ' <div class="section"> <h2>Ready to Create the Next Top Post?</h2> <p style="font-size: 16px; margin-bottom: 25px; color: #64748b;">Share your thoughts, insights, and experiences with the Knobly Cream community. Your next post could be featured as the top post of the week!</p> <a href="https://knoblycream.com/stream.php" class="cta-button cta-primary">Create New Post</a> </div>'; // Footer $html .= ' <div class="footer"> <p><strong>Knobly Cream Community</strong></p> <p>This email was sent to ' . htmlspecialchars($user['email']) . '</p> <p>© ' . date('Y') . ' Knobly Cream. All rights reserved.</p> <p><a href="https://knoblycream.com/weeklyEmail/unsubscribe.php?token={{UNSUBSCRIBE_TOKEN}}">Unsubscribe</a> | <a href="https://knoblycream.com">Visit Website</a></p> </div> </div> </body> </html>'; return $html; } /** * Generate personalized motivational message based on user activity * * @param array $activity User's weekly activity * @return string HTML content with motivational message */ function generateMotivationalMessage($activity) { $html = '<div class="motivation">'; // User has been active if ($activity['number_of_posts'] > 0 || $activity['number_of_articles'] > 0) { if ($activity['likes_received'] > 5) { $html .= '<p>Amazing work this week! Your content is resonating with the community. Keep it up!</p>'; } elseif ($activity['number_of_posts'] >= 3) { $html .= '<p>You\'ve been on fire this week with your posts! Your consistent contribution makes the Knobly Cream community thrive.</p>'; } elseif ($activity['number_of_articles'] >= 3) { $html .= '<p>You\'ve collected some great articles this week! Your curated content helps everyone discover valuable information.</p>'; } else { $html .= '<p>Great job staying active this week! Your participation helps make our community vibrant.</p>'; } } // User has been inactive else { $html .= '<p>We\'ve missed your contributions this week! The community values your voice and perspective. Why not share something today?</p>'; } $html .= '</div>'; return $html; }