forked from kevinowino869/mitrobill
Enhancement and Improvements
Refactor CSRF class: improve token handling and update session variable names Replace bulk message with ajax based message sending. Added support for multiple recipients in bulk message, and also router based filtering. Add support for multiple recipients in bulk message from customer list as requested by one of our Member. you can now send messages to multiple recipients at once from customer list. Added Exception for CRON but not tested yet. i dont have multiple routers. Added notify to know if cron has been executed or not.
This commit is contained in:
@ -22,7 +22,7 @@ switch ($action) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$appUrl = APP_URL;
|
||||
$appUrl = APP_URL;
|
||||
|
||||
$select2_customer = <<<EOT
|
||||
<script>
|
||||
@ -74,22 +74,22 @@ EOT;
|
||||
$message = str_replace('[[user_name]]', $c['username'], $message);
|
||||
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
|
||||
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
|
||||
if (strpos($message, '[[payment_link]]') !== false) {
|
||||
// token only valid for 1 day, for security reason
|
||||
$token = User::generateToken($c['id'], 1);
|
||||
if (!empty($token['token'])) {
|
||||
$tur = ORM::for_table('tbl_user_recharges')
|
||||
->where('customer_id', $c['id'])
|
||||
//->where('namebp', $package)
|
||||
->find_one();
|
||||
if ($tur) {
|
||||
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
|
||||
$message = str_replace('[[payment_link]]', $url, $message);
|
||||
}
|
||||
} else {
|
||||
$message = str_replace('[[payment_link]]', '', $message);
|
||||
}
|
||||
}
|
||||
if (strpos($message, '[[payment_link]]') !== false) {
|
||||
// token only valid for 1 day, for security reason
|
||||
$token = User::generateToken($c['id'], 1);
|
||||
if (!empty($token['token'])) {
|
||||
$tur = ORM::for_table('tbl_user_recharges')
|
||||
->where('customer_id', $c['id'])
|
||||
//->where('namebp', $package)
|
||||
->find_one();
|
||||
if ($tur) {
|
||||
$url = '?_route=home&recharge=' . $tur['id'] . '&uid=' . urlencode($token['token']);
|
||||
$message = str_replace('[[payment_link]]', $url, $message);
|
||||
}
|
||||
} else {
|
||||
$message = str_replace('[[payment_link]]', '', $message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Send the message
|
||||
@ -113,158 +113,274 @@ EOT;
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
|
||||
}
|
||||
|
||||
$ui->assign('routers', ORM::forTable('tbl_routers')->where('enabled', '1')->find_many());
|
||||
$ui->display('admin/message/bulk.tpl');
|
||||
break;
|
||||
|
||||
case 'send_bulk_ajax':
|
||||
// Check user permissions
|
||||
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
|
||||
die(json_encode(['status' => 'error', 'message' => 'Permission denied']));
|
||||
}
|
||||
|
||||
set_time_limit(0);
|
||||
// Initialize counters
|
||||
|
||||
// Get request parameters
|
||||
$group = $_REQUEST['group'] ?? '';
|
||||
$message = $_REQUEST['message'] ?? '';
|
||||
$via = $_REQUEST['via'] ?? '';
|
||||
$batch = $_REQUEST['batch'] ?? 100;
|
||||
$page = $_REQUEST['page'] ?? 0;
|
||||
$router = $_REQUEST['router'] ?? null;
|
||||
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on' ? true : false;
|
||||
|
||||
if (empty($group) || empty($message) || empty($via)) {
|
||||
die(json_encode(['status' => 'error', 'message' => 'All fields are required']));
|
||||
}
|
||||
|
||||
// Get batch of customers based on group
|
||||
$startpoint = $page * $batch;
|
||||
$customers = [];
|
||||
|
||||
if (isset($router) && !empty($router)) {
|
||||
$router = ORM::for_table('tbl_routers')->find_one($router);
|
||||
if (!$router) {
|
||||
die(json_encode(['status' => 'error', 'message' => 'Invalid router']));
|
||||
}
|
||||
|
||||
$query = ORM::for_table('tbl_user_recharges')
|
||||
->left_outer_join('tbl_customers', 'tbl_user_recharges.customer_id = tbl_customers.id')
|
||||
->where('tbl_user_recharges.routers', $router->name)
|
||||
->offset($startpoint)
|
||||
->limit($batch);
|
||||
|
||||
switch ($group) {
|
||||
case 'all':
|
||||
// No additional conditions needed
|
||||
break;
|
||||
case 'new':
|
||||
$query->where_raw("DATE(recharged_on) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)");
|
||||
break;
|
||||
case 'expired':
|
||||
$query->where('tbl_user_recharges.status', 'off');
|
||||
break;
|
||||
case 'active':
|
||||
$query->where('tbl_user_recharges.status', 'on');
|
||||
break;
|
||||
}
|
||||
|
||||
$query->selects([
|
||||
['tbl_customers.phonenumber', 'phonenumber'],
|
||||
['tbl_user_recharges.customer_id', 'customer_id'],
|
||||
['tbl_customers.fullname', 'fullname'],
|
||||
]);
|
||||
$customers = $query->find_array();
|
||||
} else {
|
||||
switch ($group) {
|
||||
case 'all':
|
||||
$customers = ORM::for_table('tbl_customers')->offset($startpoint)->limit($batch)->find_array();
|
||||
break;
|
||||
case 'new':
|
||||
$customers = ORM::for_table('tbl_customers')
|
||||
->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")
|
||||
->offset($startpoint)->limit($batch)->find_array();
|
||||
break;
|
||||
case 'expired':
|
||||
$customers = ORM::for_table('tbl_user_recharges')->where('status', 'off')
|
||||
->select('customer_id')->offset($startpoint)->limit($batch)->find_array();
|
||||
break;
|
||||
case 'active':
|
||||
$customers = ORM::for_table('tbl_user_recharges')->where('status', 'on')
|
||||
->select('customer_id')->offset($startpoint)->limit($batch)->find_array();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure $customers is always an array
|
||||
if (!$customers) {
|
||||
$customers = [];
|
||||
}
|
||||
|
||||
// Calculate total customers for the group
|
||||
$totalCustomers = 0;
|
||||
if ($router) {
|
||||
switch ($group) {
|
||||
case 'all':
|
||||
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('routers', $router->routers)->count();
|
||||
break;
|
||||
case 'new':
|
||||
$totalCustomers = ORM::for_table('tbl_user_recharges')
|
||||
->where_raw("DATE(recharged_on) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)")
|
||||
->where('routers', $router->routers)
|
||||
->count();
|
||||
break;
|
||||
case 'expired':
|
||||
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('status', 'off')->where('routers', $router->routers)->count();
|
||||
break;
|
||||
case 'active':
|
||||
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('status', 'on')->where('routers', $router->routers)->count();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch ($group) {
|
||||
case 'all':
|
||||
$totalCustomers = ORM::for_table('tbl_customers')->count();
|
||||
break;
|
||||
case 'new':
|
||||
$totalCustomers = ORM::for_table('tbl_customers')
|
||||
->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")
|
||||
->count();
|
||||
break;
|
||||
case 'expired':
|
||||
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('status', 'off')->count();
|
||||
break;
|
||||
case 'active':
|
||||
$totalCustomers = ORM::for_table('tbl_user_recharges')->where('status', 'on')->count();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Send messages
|
||||
$totalSMSSent = 0;
|
||||
$totalSMSFailed = 0;
|
||||
$totalWhatsappSent = 0;
|
||||
$totalWhatsappFailed = 0;
|
||||
$totalCustomers = 0;
|
||||
$batchStatus = $_SESSION['batchStatus'];
|
||||
$page = _req('page', -1);
|
||||
$batchStatus = [];
|
||||
|
||||
if (_req('send') == 'now') {
|
||||
// Get form data
|
||||
$group = $_REQUEST['group'];
|
||||
$message = $_REQUEST['message'];
|
||||
$via = $_REQUEST['via'];
|
||||
$test = isset($_REQUEST['test']) && $_REQUEST['test'] === 'on' ? 'yes' : 'no';
|
||||
$batch = $_REQUEST['batch'];
|
||||
$delay = $_REQUEST['delay'];
|
||||
foreach ($customers as $customer) {
|
||||
$currentMessage = str_replace(
|
||||
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||
$message
|
||||
);
|
||||
|
||||
$ui->assign('group', $group);
|
||||
$ui->assign('message', $message);
|
||||
$ui->assign('via', $via);
|
||||
$ui->assign('test', $test);
|
||||
$ui->assign('batch', $batch);
|
||||
$ui->assign('delay', $delay);
|
||||
if($page<0){
|
||||
$batchStatus = [];
|
||||
$page = 0;
|
||||
$phoneNumber = preg_replace('/\D/', '', $customer['phonenumber']);
|
||||
|
||||
if (empty($phoneNumber)) {
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => '',
|
||||
'status' => 'No Phone Number'
|
||||
];
|
||||
continue;
|
||||
}
|
||||
$startpoint = $page * $batch;
|
||||
$page++;
|
||||
// Check if fields are empty
|
||||
if ($group == '' || $message == '' || $via == '') {
|
||||
r2(getUrl('message/send_bulk'), 'e', Lang::T('All fields are required'));
|
||||
|
||||
if ($test) {
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'status' => 'Test Mode',
|
||||
'message' => $currentMessage
|
||||
];
|
||||
} else {
|
||||
// Get customer details from the database based on the selected group
|
||||
if ($group == 'all') {
|
||||
$customers = ORM::for_table('tbl_customers')
|
||||
->offset($startpoint)
|
||||
->limit($batch)->find_array();
|
||||
} elseif ($group == 'new') {
|
||||
// Get customers created just a month ago
|
||||
$customers = ORM::for_table('tbl_customers')->where_raw("DATE(created_at) >= DATE_SUB(CURDATE(), INTERVAL 1 MONTH)")
|
||||
->offset($startpoint)->limit($batch)
|
||||
->find_array();
|
||||
} elseif ($group == 'expired') {
|
||||
// Get expired user recharges where status is 'off'
|
||||
$expired = ORM::for_table('tbl_user_recharges')->select('customer_id')->where('status', 'off')
|
||||
->offset($startpoint)->limit($batch)
|
||||
->find_array();
|
||||
$customer_ids = array_column($expired, 'customer_id');
|
||||
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_array();
|
||||
} elseif ($group == 'active') {
|
||||
// Get active user recharges where status is 'on'
|
||||
$active = ORM::for_table('tbl_user_recharges')->select('customer_id')->where('status', 'on')
|
||||
->offset($startpoint)->limit($batch)
|
||||
->find_array();
|
||||
$customer_ids = array_column($active, 'customer_id');
|
||||
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_array();
|
||||
if ($via == 'sms' || $via == 'both') {
|
||||
if (Message::sendSMS($customer['phonenumber'], $currentMessage)) {
|
||||
$totalSMSSent++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Sent', 'message' => $currentMessage];
|
||||
} else {
|
||||
$totalSMSFailed++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Failed', 'message' => $currentMessage];
|
||||
}
|
||||
}
|
||||
|
||||
// Set the batch size
|
||||
$batchSize = $batch;
|
||||
|
||||
// Calculate the number of batches
|
||||
$totalCustomers = count($customers);
|
||||
$totalBatches = ceil($totalCustomers / $batchSize);
|
||||
|
||||
// Loop through customers in the current batch and send messages
|
||||
foreach ($customers as $customer) {
|
||||
// Create a copy of the original message for each customer and save it as currentMessage
|
||||
$currentMessage = $message;
|
||||
$currentMessage = str_replace('[[name]]', $customer['fullname'], $currentMessage);
|
||||
$currentMessage = str_replace('[[user_name]]', $customer['username'], $currentMessage);
|
||||
$currentMessage = str_replace('[[phone]]', $customer['phonenumber'], $currentMessage);
|
||||
$currentMessage = str_replace('[[company_name]]', $config['CompanyName'], $currentMessage);
|
||||
|
||||
if(empty($customer['phonenumber'])){
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'No Phone Number'
|
||||
];
|
||||
}else
|
||||
// Send the message based on the selected method
|
||||
if ($test === 'yes') {
|
||||
// Only for testing, do not send messages to customers
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'Test Mode - Message not sent'
|
||||
];
|
||||
if ($via == 'wa' || $via == 'both') {
|
||||
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
|
||||
$totalWhatsappSent++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
|
||||
} else {
|
||||
// Send the actual messages
|
||||
if ($via == 'sms' || $via == 'both') {
|
||||
$smsSent = Message::sendSMS($customer['phonenumber'], $currentMessage);
|
||||
if ($smsSent) {
|
||||
$totalSMSSent++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'SMS Message Sent'
|
||||
];
|
||||
} else {
|
||||
$totalSMSFailed++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'SMS Message Failed'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($via == 'wa' || $via == 'both') {
|
||||
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentMessage);
|
||||
if ($waSent) {
|
||||
$totalWhatsappSent++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'WhatsApp Message Sent'
|
||||
];
|
||||
} else {
|
||||
$totalWhatsappFailed++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'WhatsApp Message Failed'
|
||||
];
|
||||
}
|
||||
}
|
||||
$totalWhatsappFailed++;
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$ui->assign('page', $page);
|
||||
$ui->assign('totalCustomers', $totalCustomers);
|
||||
$_SESSION['batchStatus'] = $batchStatus;
|
||||
$ui->assign('batchStatus', $batchStatus);
|
||||
$ui->assign('totalSMSSent', $totalSMSSent);
|
||||
$ui->assign('totalSMSFailed', $totalSMSFailed);
|
||||
$ui->assign('totalWhatsappSent', $totalWhatsappSent);
|
||||
$ui->assign('totalWhatsappFailed', $totalWhatsappFailed);
|
||||
$ui->display('admin/message/bulk.tpl');
|
||||
|
||||
// Calculate if there are more customers to process
|
||||
$hasMore = ($startpoint + $batch) < $totalCustomers;
|
||||
|
||||
// Return JSON response
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'page' => $page + 1,
|
||||
'batchStatus' => $batchStatus,
|
||||
'message' => $currentMessage,
|
||||
'totalSent' => $totalSMSSent + $totalWhatsappSent,
|
||||
'totalFailed' => $totalSMSFailed + $totalWhatsappFailed,
|
||||
'hasMore' => $hasMore
|
||||
]);
|
||||
break;
|
||||
|
||||
case 'send_bulk_selected':
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
// Set headers
|
||||
header('Content-Type: application/json');
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||
|
||||
// Get the posted data
|
||||
$customerIds = $_POST['customer_ids'] ?? [];
|
||||
$via = $_POST['message_type'] ?? '';
|
||||
$message = isset($_POST['message']) ? trim($_POST['message']) : '';
|
||||
if (empty($customerIds) || empty($message) || empty($via)) {
|
||||
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid customer IDs, Message, or Message Type.')]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Prepare to send messages
|
||||
$sentCount = 0;
|
||||
$failedCount = 0;
|
||||
$subject = Lang::T('Notification Message');
|
||||
$form = 'Admin';
|
||||
|
||||
foreach ($customerIds as $customerId) {
|
||||
$customer = ORM::for_table('tbl_customers')->where('id', $customerId)->find_one();
|
||||
if ($customer) {
|
||||
$messageSent = false;
|
||||
|
||||
// Check the message type and send accordingly
|
||||
try {
|
||||
if ($via === 'sms' || $via === 'all') {
|
||||
$messageSent = Message::sendSMS($customer['phonenumber'], $message);
|
||||
}
|
||||
if (!$messageSent && ($via === 'wa' || $via === 'all')) {
|
||||
$messageSent = Message::sendWhatsapp($customer['phonenumber'], $message);
|
||||
}
|
||||
if (!$messageSent && ($via === 'inbox' || $via === 'all')) {
|
||||
Message::addToInbox($customer['id'], $subject, $message, $form);
|
||||
$messageSent = true;
|
||||
}
|
||||
if (!$messageSent && ($via === 'email' || $via === 'all')) {
|
||||
$messageSent = Message::sendEmail($customer['email'], $subject, $message);
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$messageSent = false;
|
||||
$failedCount++;
|
||||
sendTelegram('Failed to send message to ' . $e->getMessage());
|
||||
_log('Failed to send message to ' . $customer['fullname'] . ': ' . $e->getMessage());
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($messageSent) {
|
||||
$sentCount++;
|
||||
} else {
|
||||
$failedCount++;
|
||||
}
|
||||
} else {
|
||||
$failedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the response
|
||||
echo json_encode([
|
||||
'status' => 'success',
|
||||
'totalSent' => $sentCount,
|
||||
'totalFailed' => $failedCount
|
||||
]);
|
||||
} else {
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['status' => 'error', 'message' => Lang::T('Invalid request method.')]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
r2(getUrl('message/send_sms'), 'e', 'action not defined');
|
||||
}
|
||||
|
Reference in New Issue
Block a user