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:
parent
60d945d87f
commit
0a3205915f
@ -8,48 +8,81 @@
|
||||
|
||||
class Csrf
|
||||
{
|
||||
private static $tokenExpiration = 1800; // 30 minutes
|
||||
private const int TOKEN_LENGTH = 16;
|
||||
private const int TOKEN_EXPIRATION = 1800;
|
||||
|
||||
public static function generateToken($length = 16)
|
||||
/**
|
||||
* Generate a CSRF token.
|
||||
*
|
||||
* @param int $length
|
||||
* @return string
|
||||
*/
|
||||
public static function generateToken(int $length = self::TOKEN_LENGTH): string
|
||||
{
|
||||
return bin2hex(random_bytes($length));
|
||||
}
|
||||
|
||||
public static function validateToken($token, $storedToken)
|
||||
/**
|
||||
* Validate the provided CSRF token against the stored token.
|
||||
*
|
||||
* @param string $token
|
||||
* @param string $storedToken
|
||||
* @return bool
|
||||
*/
|
||||
public static function validateToken(string $token, string $storedToken): bool
|
||||
{
|
||||
return hash_equals($token, $storedToken);
|
||||
}
|
||||
|
||||
public static function check($token)
|
||||
/**
|
||||
* Check if the CSRF token is valid.
|
||||
*
|
||||
* @param string|null $token
|
||||
* @return bool
|
||||
*/
|
||||
public static function check(?string $token): bool
|
||||
{
|
||||
global $config;
|
||||
if($config['csrf_enabled'] == 'yes') {
|
||||
if (isset($_SESSION['csrf_token'], $_SESSION['csrf_token_time'], $token)) {
|
||||
$storedToken = $_SESSION['csrf_token'];
|
||||
$tokenTime = $_SESSION['csrf_token_time'];
|
||||
|
||||
if (time() - $tokenTime > self::$tokenExpiration) {
|
||||
if ($config['csrf_enabled'] === 'yes') {
|
||||
if (isset($_SESSION['nux_csrf_token'], $_SESSION['nux_csrf_token_time'], $token)) {
|
||||
$storedToken = $_SESSION['nux_csrf_token'];
|
||||
$tokenTime = $_SESSION['nux_csrf_token_time'];
|
||||
|
||||
if (time() - $tokenTime > self::TOKEN_EXPIRATION) {
|
||||
self::clearToken();
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::validateToken($token, $storedToken);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
return true; // CSRF is disabled
|
||||
}
|
||||
|
||||
public static function generateAndStoreToken()
|
||||
/**
|
||||
* Generate and store a new CSRF token in the session.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function generateAndStoreToken(): string
|
||||
{
|
||||
$token = self::generateToken();
|
||||
$_SESSION['csrf_token'] = $token;
|
||||
$_SESSION['csrf_token_time'] = time();
|
||||
$_SESSION['nux_csrf_token'] = $token;
|
||||
$_SESSION['nux_csrf_token_time'] = time();
|
||||
return $token;
|
||||
}
|
||||
|
||||
public static function clearToken()
|
||||
/**
|
||||
* Clear the stored CSRF token from the session.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearToken(): void
|
||||
{
|
||||
unset($_SESSION['csrf_token'], $_SESSION['csrf_token_time']);
|
||||
unset($_SESSION['nux_csrf_token'], $_SESSION['nux_csrf_token_time']);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
||||
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'];
|
||||
|
||||
$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;
|
||||
}
|
||||
$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'));
|
||||
} 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();
|
||||
}
|
||||
|
||||
// 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);
|
||||
$currentMessage = str_replace(
|
||||
['[[name]]', '[[user_name]]', '[[phone]]', '[[company_name]]'],
|
||||
[$customer['fullname'], $customer['username'], $customer['phonenumber'], $config['CompanyName']],
|
||||
$message
|
||||
);
|
||||
|
||||
if(empty($customer['phonenumber'])){
|
||||
$phoneNumber = preg_replace('/\D/', '', $customer['phonenumber']);
|
||||
|
||||
if (empty($phoneNumber)) {
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'phone' => '',
|
||||
'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
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($test) {
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'Test Mode - Message not sent'
|
||||
'status' => 'Test Mode',
|
||||
'message' => $currentMessage
|
||||
];
|
||||
} else {
|
||||
// Send the actual messages
|
||||
if ($via == 'sms' || $via == 'both') {
|
||||
$smsSent = Message::sendSMS($customer['phonenumber'], $currentMessage);
|
||||
if ($smsSent) {
|
||||
if (Message::sendSMS($customer['phonenumber'], $currentMessage)) {
|
||||
$totalSMSSent++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'SMS Message Sent'
|
||||
];
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Sent', 'message' => $currentMessage];
|
||||
} else {
|
||||
$totalSMSFailed++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'SMS Message Failed'
|
||||
];
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'SMS Failed', 'message' => $currentMessage];
|
||||
}
|
||||
}
|
||||
|
||||
if ($via == 'wa' || $via == 'both') {
|
||||
$waSent = Message::sendWhatsapp($customer['phonenumber'], $currentMessage);
|
||||
if ($waSent) {
|
||||
if (Message::sendWhatsapp($customer['phonenumber'], $currentMessage)) {
|
||||
$totalWhatsappSent++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
'message' => $currentMessage,
|
||||
'status' => 'WhatsApp Message Sent'
|
||||
];
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Sent', 'message' => $currentMessage];
|
||||
} else {
|
||||
$totalWhatsappFailed++;
|
||||
$batchStatus[] = [
|
||||
'name' => $customer['fullname'],
|
||||
'phone' => $customer['phonenumber'],
|
||||
$batchStatus[] = ['name' => $customer['fullname'], 'phone' => $customer['phonenumber'], 'status' => 'WhatsApp Failed', 'message' => $currentMessage];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
'status' => 'WhatsApp Message Failed'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$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');
|
||||
'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');
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ if (php_sapi_name() !== 'cli') {
|
||||
echo "PHP Time\t" . date('Y-m-d H:i:s') . "\n";
|
||||
$res = ORM::raw_execute('SELECT NOW() AS WAKTU;');
|
||||
$statement = ORM::get_last_statement();
|
||||
$rows = array();
|
||||
$rows = [];
|
||||
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
|
||||
echo "MYSQL Time\t" . $row['WAKTU'] . "\n";
|
||||
}
|
||||
@ -45,62 +45,93 @@ echo "Found " . count($d) . " user(s)\n";
|
||||
run_hook('cronjob'); #HOOK
|
||||
|
||||
foreach ($d as $ds) {
|
||||
try {
|
||||
$date_now = strtotime(date("Y-m-d H:i:s"));
|
||||
$expiration = strtotime($ds['expiration'] . ' ' . $ds['time']);
|
||||
echo $ds['expiration'] . " : " . (($isCli) ? $ds['username'] : Lang::maskText($ds['username']));
|
||||
echo $ds['expiration'] . " : " . ($isCli ? $ds['username'] : Lang::maskText($ds['username']));
|
||||
|
||||
if ($date_now >= $expiration) {
|
||||
echo " : EXPIRED \r\n";
|
||||
|
||||
// Fetch user recharge details
|
||||
$u = ORM::for_table('tbl_user_recharges')->where('id', $ds['id'])->find_one();
|
||||
if (!$u) {
|
||||
throw new Exception("User recharge record not found for ID: " . $ds['id']);
|
||||
}
|
||||
|
||||
// Fetch customer details
|
||||
$c = ORM::for_table('tbl_customers')->where('id', $ds['customer_id'])->find_one();
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
|
||||
if (empty($c)) {
|
||||
if (!$c) {
|
||||
$c = $u;
|
||||
}
|
||||
|
||||
// Fetch plan details
|
||||
$p = ORM::for_table('tbl_plans')->where('id', $u['plan_id'])->find_one();
|
||||
if (!$p) {
|
||||
throw new Exception("Plan not found for ID: " . $u['plan_id']);
|
||||
}
|
||||
|
||||
$dvc = Package::getDevice($p);
|
||||
if ($_app_stage != 'demo') {
|
||||
if (file_exists($dvc)) {
|
||||
require_once $dvc;
|
||||
try {
|
||||
(new $p['device'])->remove_customer($c, $p);
|
||||
} catch (Throwable $e) {
|
||||
_log($e->getMessage());
|
||||
sendTelegram($e->getMessage());
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "Cron error Devices $p[device] not found, cannot disconnect $c[username]";
|
||||
Message::sendTelegram("Cron error Devices $p[device] not found, cannot disconnect $c[username]");
|
||||
throw new Exception("Cron error: Devices " . $p['device'] . "not found, cannot disconnect ".$c['username']."\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Send notification and update user status
|
||||
try {
|
||||
echo Message::sendPackageNotification($c, $u['namebp'], $p['price'], $textExpired, $config['user_notification_expired']) . "\n";
|
||||
//update database user dengan status off
|
||||
$u->status = 'off';
|
||||
$u->save();
|
||||
} catch (Throwable $e) {
|
||||
_log($e->getMessage());
|
||||
sendTelegram($e->getMessage());
|
||||
echo "Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
// autorenewal from deposit
|
||||
// Auto-renewal from deposit
|
||||
if ($config['enable_balance'] == 'yes' && $c['auto_renewal']) {
|
||||
list($bills, $add_cost) = User::getBills($ds['customer_id']);
|
||||
[$bills, $add_cost] = User::getBills($ds['customer_id']);
|
||||
if ($add_cost != 0) {
|
||||
if (!empty($add_cost)) {
|
||||
$p['price'] += $add_cost;
|
||||
}
|
||||
}
|
||||
|
||||
if ($p && $c['balance'] >= $p['price']) {
|
||||
if (Package::rechargeUser($ds['customer_id'], $ds['routers'], $p['id'], 'Customer', 'Balance')) {
|
||||
// if success, then get the balance
|
||||
Balance::min($ds['customer_id'], $p['price']);
|
||||
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
echo "auto renewall Success\n";
|
||||
echo "plan enabled: " . (string) $p['enabled'] . " | User balance: " . (string) $c['balance'] . " | price " . (string) $p['price'] . "\n";
|
||||
echo "auto renewal Success\n";
|
||||
} else {
|
||||
echo "plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
echo "auto renewall Failed\n";
|
||||
Message::sendTelegram("FAILED RENEWAL #cron\n\n#u$c[username] #buy #Hotspot \n" . $p['name_plan'] .
|
||||
echo "plan enabled: " . $p['enabled'] . " | User balance: " . $c['balance'] . " | price " . $p['price'] . "\n";
|
||||
echo "auto renewal Failed\n";
|
||||
Message::sendTelegram("FAILED RENEWAL #cron\n\n#u." . $c['username'] . " #buy #Hotspot \n" . $p['name_plan'] .
|
||||
"\nRouter: " . $p['routers'] .
|
||||
"\nPrice: " . $p['price']);
|
||||
}
|
||||
} else {
|
||||
echo "no renewall | plan enabled: $p[enabled] | User balance: $c[balance] | price $p[price]\n";
|
||||
echo "no renewal | plan enabled: " . (string) $p['enabled'] . " | User balance: " . (string) $c['balance'] . " | price " . (string) $p['price'] . "\n";
|
||||
}
|
||||
} else {
|
||||
echo "no renewall | balance $config[enable_balance] auto_renewal $c[auto_renewal]\n";
|
||||
echo "no renewal | balance" . $config['enable_balance'] . " auto_renewal " . $c['auto_renewal'] . "\n";
|
||||
}
|
||||
} else {
|
||||
echo " : ACTIVE \r\n";
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
// Catch any unexpected errors
|
||||
_log($e->getMessage());
|
||||
sendTelegram($e->getMessage());
|
||||
echo "Unexpected Error: " . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
//Cek interim-update radiusrest
|
||||
@ -207,14 +238,7 @@ if ($config['router_check']) {
|
||||
Message::SendEmail($adminEmail, $subject, $message);
|
||||
sendTelegram($message);
|
||||
}
|
||||
echo "Router monitoring finished\n";
|
||||
}
|
||||
|
||||
|
||||
if (defined('PHP_SAPI') && PHP_SAPI === 'cli') {
|
||||
echo "Cronjob finished\n";
|
||||
} else {
|
||||
echo "</pre>";
|
||||
echo "Router monitoring finished checking.\n";
|
||||
}
|
||||
|
||||
flock($lock, LOCK_UN);
|
||||
@ -224,5 +248,5 @@ unlink($lockFile);
|
||||
$timestampFile = "$UPLOAD_PATH/cron_last_run.txt";
|
||||
file_put_contents($timestampFile, time());
|
||||
|
||||
|
||||
run_hook('cronjob_end'); #HOOK
|
||||
echo "Cron job finished and completed successfully.\n";
|
@ -97,6 +97,7 @@
|
||||
<table id="customerTable" class="table table-bordered table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="select-all"></th>
|
||||
<th>{Lang::T('Username')}</th>
|
||||
<th>Photo</th>
|
||||
<th>{Lang::T('Account Type')}</th>
|
||||
@ -114,6 +115,7 @@
|
||||
<tbody>
|
||||
{foreach $d as $ds}
|
||||
<tr {if $ds['status'] !='Active' }class="danger" {/if}>
|
||||
<td><input type="checkbox" name="customer_ids[]" value="{$ds['id']}"></td>
|
||||
<td onclick="window.location.href = '{Text::url('customers/view/', $ds['id'])}'"
|
||||
style="cursor:pointer;">{$ds['username']}</td>
|
||||
<td>
|
||||
@ -161,17 +163,173 @@
|
||||
<a href="{Text::url('customers/sync/', $ds['id'], '&token=', $csrf_token)}"
|
||||
id="{$ds['id']}" style="margin: 5px; color:black"
|
||||
class="btn btn-success btn-xs"> {Lang::T('Sync')} </a>
|
||||
<a href="{Text::url('plan/recharge/', $ds['id'], '&token=', $csrf_token)}" id="{$ds['id']}"
|
||||
style="margin: 0px;" class="btn btn-primary btn-xs">{Lang::T('Recharge')}</a>
|
||||
<a href="{Text::url('plan/recharge/', $ds['id'], '&token=', $csrf_token)}"
|
||||
id="{$ds['id']}" style="margin: 0px;"
|
||||
class="btn btn-primary btn-xs">{Lang::T('Recharge')}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row" style="padding: 5px">
|
||||
<div class="col-lg-3 col-lg-offset-9">
|
||||
<div class="btn-group btn-group-justified" role="group">
|
||||
<!-- <div class="btn-group" role="group">
|
||||
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
|
||||
<button id="deleteSelectedTokens" class="btn btn-danger">{Lang::T('Delete
|
||||
Selected')}</button>
|
||||
{/if}
|
||||
</div> -->
|
||||
<div class="btn-group" role="group">
|
||||
<button id="sendMessageToSelected" class="btn btn-success">{Lang::T('Send
|
||||
Message')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{include file="pagination.tpl"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Modal for Sending Messages -->
|
||||
<div id="sendMessageModal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="sendMessageModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="sendMessageModalLabel">{Lang::T('Send Message')}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<select id="messageType" class="form-control">
|
||||
<option value="all">{Lang::T('All')}</option>
|
||||
<option value="email">{Lang::T('Email')}</option>
|
||||
<option value="inbox">{Lang::T('Inbox')}</option>
|
||||
<option value="sms">{Lang::T('SMS')}</option>
|
||||
<option value="wa">{Lang::T('WhatsApp')}</option>
|
||||
</select>
|
||||
<br>
|
||||
<textarea id="messageContent" class="form-control" rows="4"
|
||||
placeholder="{Lang::T('Enter your message here...')}"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{Lang::T('Close')}</button>
|
||||
<button type="button" id="sendMessageButton" class="btn btn-primary">{Lang::T('Send Message')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script>
|
||||
// Select or deselect all checkboxes
|
||||
document.getElementById('select-all').addEventListener('change', function () {
|
||||
var checkboxes = document.querySelectorAll('input[name="customer_ids[]"]');
|
||||
for (var checkbox of checkboxes) {
|
||||
checkbox.checked = this.checked;
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
let selectedCustomerIds = [];
|
||||
|
||||
// Collect selected customer IDs when the button is clicked
|
||||
$('#sendMessageToSelected').on('click', function () {
|
||||
selectedCustomerIds = $('input[name="customer_ids[]"]:checked').map(function () {
|
||||
return $(this).val();
|
||||
}).get();
|
||||
|
||||
if (selectedCustomerIds.length === 0) {
|
||||
Swal.fire({
|
||||
title: 'Error!',
|
||||
text: 'Please select at least one customer to send a message.',
|
||||
icon: 'error',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the modal
|
||||
$('#sendMessageModal').modal('show');
|
||||
});
|
||||
|
||||
// Handle sending the message
|
||||
$('#sendMessageButton').on('click', function () {
|
||||
const message = $('#messageContent').val().trim();
|
||||
const messageType = $('#messageType').val();
|
||||
|
||||
if (!message) {
|
||||
Swal.fire({
|
||||
title: 'Error!',
|
||||
text: 'Please enter a message to send.',
|
||||
icon: 'error',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the button and show loading text
|
||||
$(this).prop('disabled', true).text('Sending...');
|
||||
|
||||
$.ajax({
|
||||
url: '?_route=message/send_bulk_selected',
|
||||
method: 'POST',
|
||||
data: {
|
||||
customer_ids: selectedCustomerIds,
|
||||
message_type: messageType,
|
||||
message: message
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (response) {
|
||||
// Handle success response
|
||||
if (response.status === 'success') {
|
||||
Swal.fire({
|
||||
title: 'Success!',
|
||||
text: 'Message sent successfully.',
|
||||
icon: 'success',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: 'Error!',
|
||||
text: 'Error sending message: ' + response.message,
|
||||
icon: 'error',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
}
|
||||
$('#sendMessageModal').modal('hide');
|
||||
$('#messageContent').val(''); // Clear the message content
|
||||
},
|
||||
error: function () {
|
||||
Swal.fire({
|
||||
title: 'Error!',
|
||||
text: 'Failed to send the message. Please try again.',
|
||||
icon: 'error',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
},
|
||||
complete: function () {
|
||||
// Re-enable the button and reset text
|
||||
$('#sendMessageButton').prop('disabled', false).text('{Lang::T('Send Message')}');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$(document).ready(function () {
|
||||
$('#sendMessageModal').on('show.bs.modal', function () {
|
||||
$(this).attr('inert', 'true');
|
||||
});
|
||||
$('#sendMessageModal').on('shown.bs.modal', function () {
|
||||
$('#messageContent').focus();
|
||||
$(this).removeAttr('inert');
|
||||
});
|
||||
$('#sendMessageModal').on('hidden.bs.modal', function () {
|
||||
// $('#button').focus();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{include file = "sections/footer.tpl" }
|
@ -1,29 +1,34 @@
|
||||
{include file="sections/header.tpl"}
|
||||
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.11.3/css/jquery.dataTables.min.css">
|
||||
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
{if $page>0 && $totalCustomers>0}
|
||||
<div class="alert alert-info" role="alert"><span class="loading"></span> {Lang::T("Sending message in progress. Don't close this page.")}</div>
|
||||
{/if}
|
||||
<div id="status" class="mb-3"></div>
|
||||
<div class="panel panel-primary panel-hovered panel-stacked mb30 {if $page>0 && $totalCustomers >0}hidden{/if}">
|
||||
<div class="panel-heading">{Lang::T('Send Bulk Message')}</div>
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" method="get" role="form" id="bulkMessageForm" action="">
|
||||
<input type="hidden" name="page" value="{if $page>0 && $totalCustomers==0}-1{else}{$page}{/if}">
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('Router')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control select2" name="router" id="router">
|
||||
<option value="">{Lang::T('All Routers')}</option>
|
||||
{foreach $routers as $router}
|
||||
<option value="{$router['id']}">{$router['name']}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('Group')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="group" id="group">
|
||||
<option value="all" {if $group == 'all'}selected{/if}>{Lang::T('All Customers')}
|
||||
</option>
|
||||
<option value="new" {if $group == 'new'}selected{/if}>{Lang::T('New Customers')}
|
||||
</option>
|
||||
<option value="expired" {if $group == 'expired'}selected{/if}>
|
||||
{Lang::T('Expired Customers')}</option>
|
||||
<option value="active" {if $group == 'active'}selected{/if}>
|
||||
{Lang::T('Active Customers')}</option>
|
||||
<option value="all" {if $group=='all' }selected{/if}>{Lang::T('All Customers')}</option>
|
||||
<option value="new" {if $group=='new' }selected{/if}>{Lang::T('New Customers')}</option>
|
||||
<option value="expired" {if $group=='expired' }selected{/if}>{Lang::T('Expired Customers')}</option>
|
||||
<option value="active" {if $group=='active' }selected{/if}>{Lang::T('Active Customers')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -33,8 +38,7 @@
|
||||
<select class="form-control" name="via" id="via">
|
||||
<option value="sms" {if $via=='sms' }selected{/if}>{Lang::T('SMS')}</option>
|
||||
<option value="wa" {if $via=='wa' }selected{/if}>{Lang::T('WhatsApp')}</option>
|
||||
<option value="both" {if $via == 'both'}selected{/if}>{Lang::T('SMS and WhatsApp')}
|
||||
</option>
|
||||
<option value="both" {if $via=='both' }selected{/if}>{Lang::T('SMS and WhatsApp')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,27 +54,15 @@
|
||||
<option value="40" {if $batch=='40' }selected{/if}>{Lang::T('40 Messages')}</option>
|
||||
<option value="50" {if $batch=='50' }selected{/if}>{Lang::T('50 Messages')}</option>
|
||||
<option value="60" {if $batch=='60' }selected{/if}>{Lang::T('60 Messages')}</option>
|
||||
</select>{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('Delay')}</label>
|
||||
<div class="col-md-6">
|
||||
<select class="form-control" name="delay" id="delay">
|
||||
<option value="1" {if $delay == '1'}selected{/if}>{Lang::T('No Delay')}</option>
|
||||
<option value="5" {if $delay == '5'}selected{/if}>{Lang::T('5 Seconds')}</option>
|
||||
<option value="10" {if $delay == '10'}selected{/if}>{Lang::T('10 Seconds')}</option>
|
||||
<option value="15" {if $delay == '15'}selected{/if}>{Lang::T('15 Seconds')}</option>
|
||||
<option value="20" {if $delay == '20'}selected{/if}>{Lang::T('20 Seconds')}</option>
|
||||
</select>{Lang::T('Use at least 5 secs if you are sending to all customers to avoid being banned by your message provider')}
|
||||
</select>
|
||||
{Lang::T('Use 20 and above if you are sending to all customers to avoid server time out')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{Lang::T('Message')}</label>
|
||||
<div class="col-md-6">
|
||||
<textarea class="form-control" id="message" name="message" required
|
||||
placeholder="{Lang::T('Compose your message...')}" rows="5">{$message}</textarea>
|
||||
<input name="test" type="checkbox">
|
||||
<textarea class="form-control" id="message" name="message" required placeholder="{Lang::T('Compose your message...')}" rows="5">{$message}</textarea>
|
||||
<input name="test" id="test" type="checkbox">
|
||||
{Lang::T('Testing [if checked no real message is sent]')}
|
||||
</div>
|
||||
<p class="help-block col-md-4">
|
||||
@ -87,94 +79,141 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-offset-2 col-lg-10">
|
||||
{if $page >= 0}
|
||||
<button class="btn btn-success" id="submit" type="submit" name=send value=now>
|
||||
{Lang::T('Send Message')}</button>
|
||||
{else}
|
||||
<button class="btn btn-success"
|
||||
onclick="return ask(this, 'Continue the process of sending mass messages?')"
|
||||
type="submit" name=send value=now>
|
||||
{Lang::T('Send Message')}</button>
|
||||
{/if}
|
||||
<button type="button" id="startBulk" class="btn btn-primary">Start Bulk Messaging</button>
|
||||
<a href="{Text::url('dashboard')}" class="btn btn-default">{Lang::T('Cancel')}</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{if $batchStatus}
|
||||
<p><span class="label label-success">{Lang::T('Total SMS Sent')}: {$totalSMSSent}</span> <span
|
||||
class="label label-danger">{Lang::T('Total SMS
|
||||
Failed')}: {$totalSMSFailed}</span> <span class="label label-success">{Lang::T('Total WhatsApp Sent')}:
|
||||
{$totalWhatsappSent}</span> <span class="label label-danger">{Lang::T('Total WhatsApp Failed')}:
|
||||
{$totalWhatsappFailed}</span></p>
|
||||
{/if}
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<h3 class="box-title">{Lang::T('Message Results')}</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<table id="messageResultsTable" class="table table-bordered table-striped table-condensed">
|
||||
<!-- Add a Table for Sent History -->
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{Lang::T('Message Sending History')}</div>
|
||||
<div class="panel-body">
|
||||
<div id="status"></div>
|
||||
<table class="table table-bordered" id="historyTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{Lang::T('Name')}</th>
|
||||
<th>{Lang::T('Customer')}</th>
|
||||
<th>{Lang::T('Phone')}</th>
|
||||
<th>{Lang::T('Message')}</th>
|
||||
<th>{Lang::T('Status')}</th>
|
||||
<th>{Lang::T('Message')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{foreach $batchStatus as $customer}
|
||||
<tr>
|
||||
<td>{$customer.name}</td>
|
||||
<td>{$customer.phone}</td>
|
||||
<td>{$customer.message}</td>
|
||||
<td>{$customer.status}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.11.3/js/jquery.dataTables.min.js"></script>
|
||||
{literal}
|
||||
<script>
|
||||
var $j = jQuery.noConflict();
|
||||
let page = 0;
|
||||
let totalSent = 0;
|
||||
let totalFailed = 0;
|
||||
let hasMore = true;
|
||||
|
||||
$j(document).ready(function() {
|
||||
$j('#messageResultsTable').DataTable();
|
||||
// Initialize DataTable
|
||||
let historyTable = $('#historyTable').DataTable({
|
||||
paging: true,
|
||||
searching: true,
|
||||
ordering: true,
|
||||
info: true,
|
||||
autoWidth: false,
|
||||
responsive: true
|
||||
});
|
||||
|
||||
{if $page>0 && $totalCustomers >0}
|
||||
setTimeout(() => {
|
||||
document.getElementById('submit').click();
|
||||
}, {$delay}000);
|
||||
{/if}
|
||||
{if $page>0 && $totalCustomers==0}
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Bulk Send Done',
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 5000,
|
||||
timerProgressBar: true,
|
||||
didOpen: (toast) => {
|
||||
toast.addEventListener('mouseenter', Swal.stopTimer)
|
||||
toast.addEventListener('mouseleave', Swal.resumeTimer)
|
||||
function sendBatch() {
|
||||
if (!hasMore) return;
|
||||
|
||||
$.ajax({
|
||||
url: '?_route=message/send_bulk_ajax',
|
||||
method: 'POST',
|
||||
data: {
|
||||
group: $('#group').val(),
|
||||
message: $('#message').val(),
|
||||
via: $('#via').val(),
|
||||
batch: $('#batch').val(),
|
||||
router: $('#router').val() || '',
|
||||
page: page,
|
||||
test: $('#test').is(':checked') ? 'on' : 'off'
|
||||
},
|
||||
dataType: 'json',
|
||||
beforeSend: function () {
|
||||
$('#status').html(`
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-spinner fa-spin"></i> Sending batch ${page + 1}...
|
||||
</div>
|
||||
`);
|
||||
},
|
||||
success: function (response) {
|
||||
console.log("Response received:", response);
|
||||
|
||||
if (response && response.status === 'success') {
|
||||
totalSent += response.totalSent || 0;
|
||||
totalFailed += response.totalFailed || 0;
|
||||
page = response.page || 0;
|
||||
hasMore = response.hasMore || false;
|
||||
|
||||
$('#status').html(`
|
||||
<div class="alert alert-success">
|
||||
<i class="fas fa-check-circle"></i> Batch ${page} sent! (Total Sent: ${totalSent}, Failed: ${totalFailed})
|
||||
</div>
|
||||
`);
|
||||
|
||||
(response.batchStatus || []).forEach(msg => {
|
||||
let statusClass = msg.status.includes('Failed') ? 'danger' : 'success';
|
||||
historyTable.row.add([
|
||||
msg.name,
|
||||
msg.phone,
|
||||
`<span class="text-${statusClass}">${msg.status}</span>`,
|
||||
msg.message || 'No message'
|
||||
]).draw(false); // Add row without redrawing the table
|
||||
});
|
||||
|
||||
if (hasMore) {
|
||||
sendBatch();
|
||||
} else {
|
||||
$('#status').html(`
|
||||
<div class="alert alert-success">
|
||||
<i class="fas fa-check-circle"></i> All batches sent! Total Sent: ${totalSent}, Failed: ${totalFailed}
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
} else {
|
||||
console.error("Unexpected response format:", response);
|
||||
$('#status').html(`
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-exclamation-circle"></i> Error: Unexpected response format.
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
$('#status').html(`
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-exclamation-circle"></i> Error: Failed to send batch ${page + 1}.
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
});
|
||||
{/if}
|
||||
}
|
||||
|
||||
// Start sending on button click
|
||||
$('#startBulk').on('click', function () {
|
||||
page = 0;
|
||||
totalSent = 0;
|
||||
totalFailed = 0;
|
||||
hasMore = true;
|
||||
$('#status').html('<div class="alert alert-info"><i class="fas fa-spinner fa-spin"></i> Starting bulk message sending...</div>');
|
||||
historyTable.clear().draw(); // Clear history table before starting
|
||||
sendBatch();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
{/literal}
|
||||
|
||||
{include file="sections/footer.tpl"}
|
Loading…
x
Reference in New Issue
Block a user