Add Message Feature

you can now send single and Bulk SMS, WhatsApp or Both WA/SMS message to customers based on their groups, more groups will be added later
This commit is contained in:
Focuslinkstech 2024-03-18 01:35:48 +01:00
parent dbc3a2623c
commit 518917aac6
4 changed files with 527 additions and 171 deletions

View File

@ -0,0 +1,212 @@
<?php
/**
* PHP Mikrotik Billing (https://github.com/hotspotbilling/phpnuxbill/)
* by https://t.me/ibnux
**/
_admin();
$ui->assign('_title', Lang::T('Send Message'));
$ui->assign('_system_menu', 'message');
$action = $routes['1'];
$ui->assign('_admin', $admin);
if (empty($action)) {
$action = 'send';
}
switch ($action) {
case 'send':
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
$select2_customer = <<<EOT
<script>
document.addEventListener("DOMContentLoaded", function(event) {
$('#personSelect').select2({
theme: "bootstrap",
ajax: {
url: function(params) {
if(params.term != undefined){
return './index.php?_route=autoload/customer_select2&s='+params.term;
}else{
return './index.php?_route=autoload/customer_select2';
}
}
}
});
});
</script>
EOT;
$c = ORM::for_table('tbl_customers')->find_many();
$ui->assign('c', $c);
$ui->assign('xfooter', $select2_customer);
$ui->display('message.tpl');
break;
case 'send-post':
// Check user permissions
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
// Get form data
$id_customer = $_POST['id_customer'];
$message = $_POST['message'];
$via = $_POST['via'];
// Check if fields are empty
if ($id_customer == '' or $message == '' or $via == '') {
r2(U . 'message/send', 'e', Lang::T('All field is required'));
} else {
// Get customer details from the database
$c = ORM::for_table('tbl_customers')->find_one($id_customer);
// Replace placeholders in the message with actual values
$message = str_replace('[[name]]', $c['fullname'], $message);
$message = str_replace('[[user_name]]', $c['username'], $message);
$message = str_replace('[[phone]]', $c['phonenumber'], $message);
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
//Send the message
if ($via == 'sms' || $via == 'both') {
$smsSent = Message::sendSMS($c['phonenumber'], $message);
}
if ($via == 'wa' || $via == 'both') {
$waSent = Message::sendWhatsapp($c['phonenumber'], $message);
}
if (isset($smsSent) || isset($waSent)) {
r2(U . 'message/send', 's', Lang::T('Message Sent Successfully'));
} else {
r2(U . 'message/send', 'e', Lang::T('Failed to send message'));
}
}
break;
case 'send_bulk':
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->display('message-bulk.tpl');
break;
case 'send_bulk-post':
// Check user permissions
if (!in_array($admin['user_type'], ['SuperAdmin', 'Admin', 'Agent', 'Sales'])) {
_alert(Lang::T('You do not have permission to access this page'), 'danger', "dashboard");
}
// Get form data
$group = $_POST['group'];
$message = $_POST['message'];
$via = $_POST['via'];
// Initialize counters
$successCount = 0;
$failCount = 0;
$successMessages = [];
$failMessages = [];
// Check if fields are empty
if ($group == '' or $message == '' or $via == '') {
r2(U . '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')->find_many();
} 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)")->find_many();
} elseif ($group == 'expired') {
// Get expired user recharges where status is 'off'
$expired = ORM::for_table('tbl_user_recharges')->where('status', 'off')->find_many();
$customer_ids = [];
foreach ($expired as $recharge) {
$customer_ids[] = $recharge->customer_id;
}
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_many();
} elseif ($group == 'active') {
// Get active user recharges where status is 'on'
$active = ORM::for_table('tbl_user_recharges')->where('status', 'on')->find_many();
$customer_ids = [];
foreach ($active as $recharge) {
$customer_ids[] = $recharge->customer_id;
}
$customers = ORM::for_table('tbl_customers')->where_in('id', $customer_ids)->find_many();
}
// Loop through customers and send messages
foreach ($customers as $customer) {
// Replace placeholders in the message with actual values for each customer
$message = str_replace('[[name]]', $customer['fullname'], $message);
$message = str_replace('[[user_name]]', $customer['username'], $message);
$message = str_replace('[[phone]]', $customer['phonenumber'], $message);
$message = str_replace('[[company_name]]', $config['CompanyName'], $message);
// Send the message based on the selected method
if ($via == 'sms' || $via == 'both') {
$smsSent = Message::sendSMS($customer['phonenumber'], $message);
if ($smsSent) {
$successCount++;
$successMessages[] = "SMS sent to {$customer['fullname']}: {$customer['phonenumber']}";
} else {
$failCount++;
$failMessages[] = "Failed to send SMS to {$customer['fullname']}: {$customer['phonenumber']}";
}
// Introduce a delay of 5 seconds between each SMS
sleep(5);
}
if ($via == 'wa' || $via == 'both') {
$waSent = Message::sendWhatsapp($customer['phonenumber'], $message);
if ($waSent) {
$successCount++;
$successMessages[] = "WhatsApp message sent to {$customer['fullname']}: {$customer['phonenumber']}";
} else {
$failCount++;
$failMessages[] = "Failed to send WhatsApp message to {$customer['fullname']}: {$customer['phonenumber']}";
}
// Introduce a delay of 5 seconds between each WhatsApp message
sleep(5);
}
}
$responseMessage = '';
if ($successCount > 0) {
$responseMessage .= "Messages Sent Successfully: {$successCount}<br>";
$responseMessage .= "<ul>";
foreach ($successMessages as $successMessage) {
$responseMessage .= "<li>{$successMessage}</li>";
}
$responseMessage .= "</ul>";
}
if ($failCount > 0) {
$responseMessage .= "Failed to send messages: {$failCount}<br>";
$responseMessage .= "<ul>";
foreach ($failMessages as $failMessage) {
$responseMessage .= "<li>{$failMessage}</li>";
}
$responseMessage .= "</ul>";
}
if ($responseMessage != '') {
r2(U . 'message/send_bulk', 's', $responseMessage);
} else {
r2(U . 'message/send_bulk', 'e', Lang::T('No messages sent'));
}
}
break;
default:
r2(U . 'message/send_sms', 'e', 'action not defined');
}

62
ui/ui/message-bulk.tpl Normal file
View File

@ -0,0 +1,62 @@
{include file="sections/header.tpl"}
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="panel panel-primary panel-hovered panel-stacked mb30">
<div class="panel-heading">{Lang::T('Send Bulk Message')}</div>
<div class="panel-body">
<form class="form-horizontal" method="post" role="form" action="{$_url}message/send_bulk-post">
<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" selected>{Lang::T('All Customers')}</option>
<option value="new">{Lang::T('New Customers')}</option>
<option value="expired">{Lang::T('Expired Customers')}</option>
<option value="active">{Lang::T('Active Customers')}</option>
</select>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
<div class="col-md-6">
<select class="form-control" name="via" id="via">
<option value="sms" selected>{Lang::T('SMS')}</option>
<option value="wa">{Lang::T('WhatsApp')}</option>
<option value="both">{Lang::T('SMS and WhatsApp')}</option>
</select>
</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" placeholder="{Lang::T('Compose your message...')}" rows="5"></textarea>
</div>
<p class="help-block col-md-4">
{Lang::T('Use placeholders:')}
<br>
<b>{Lang::T('[[name]]')}</b> - {Lang::T('Customer Name')}
<br>
<b>{Lang::T('[[user_name]]')}</b> - {Lang::T('Customer Username')}
<br>
<b>{Lang::T('[[phone]]')}</b> - {Lang::T('Customer Phone')}
<br>
<b>{Lang::T('[[company_name]]')}</b> - {Lang::T('Your Company Name')}
</p>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button class="btn btn-success" type="submit">{Lang::T('Send Message')}</button>
<a href="{$_url}dashboard" class="btn btn-default">{Lang::T('Cancel')}</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{include file="sections/footer.tpl"}

65
ui/ui/message.tpl Normal file
View File

@ -0,0 +1,65 @@
{include file="sections/header.tpl"}
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="panel panel-primary panel-hovered panel-stacked mb30">
<div class="panel-heading">{Lang::T('Send Personal Message')}</div>
<div class="panel-body">
<form class="form-horizontal" method="post" role="form" action="{$_url}message/send-post" >
<div class="form-group">
<label class="col-md-2 control-label">{Lang::T('Customer')}</label>
<div class="col-md-6">
<select id="personSelect" class="form-control select2" name="id_customer" style="width: 100%" data-placeholder="Select a customer...">
<option></option>
{foreach $c as $cs}
{if $id eq $cs['id']}
<option value="{$cs['id']}" selected>{$cs['username']}</option>
{else}
<option value="{$cs['id']}">{$cs['username']}</option>
{/if}
{/foreach}
</select>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label">{Lang::T('Send Via')}</label>
<div class="col-md-6">
<select class="form-control" name="via" id="via">
<option value="sms" selected> {Lang::T('SMS')}</option>
<option value="wa"> {Lang::T('WhatsApp')}</option>
<option value="both"> {Lang::T('SMS and WhatsApp')}</option>
</select>
</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" placeholder="{Lang::T('Compose your message...')}" rows="5"></textarea>
</div>
<p class="help-block col-md-4">
{Lang::T('Use placeholders:')}
<br>
<b>{Lang::T('[[name]]')}</b> - {Lang::T('Customer Name')}
<br>
<b>{Lang::T('[[user_name]]')}</b> - {Lang::T('Customer Username')}
<br>
<b>{Lang::T('[[phone]]')}</b> - {Lang::T('Customer Phone')}
<br>
<b>{Lang::T('[[company_name]]')}</b> - {Lang::T('Your Company Name')}
</p>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button class="btn btn-success" type="submit">{Lang::T('Send Message')}</button>
<a href="{$_url}dashboard" class="btn btn-default">{Lang::T('Cancel')}</a>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{include file="sections/footer.tpl"}

View File

@ -150,7 +150,7 @@
<aside class="main-sidebar">
<section class="sidebar">
<ul class="sidebar-menu" data-widget="tree">
<li {if $_system_menu eq 'dashboard'}class="active" {/if}>
<li {if $_system_menu eq 'dashboard' }class="active" {/if}>
<a href="{$_url}dashboard">
<i class="ion ion-monitor"></i>
<span>{Lang::T('Dashboard')}</span>
@ -158,7 +158,7 @@
</li>
{$_MENU_AFTER_DASHBOARD}
{if !in_array($_admin['user_type'],['Report'])}
<li {if $_system_menu eq 'customers'}class="active" {/if}>
<li {if $_system_menu eq 'customers' }class="active" {/if}>
<a href="{$_url}customers">
<i class="fa fa-users"></i>
<span>{Lang::T('Customer')}</span>
@ -173,18 +173,18 @@
</span>
</a>
<ul class="treeview-menu">
<li {if $_routes[1] eq 'list'}class="active" {/if}><a
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
href="{$_url}plan/list">{Lang::T('Active Users')}</a></li>
{if $_c['disable_voucher'] != 'yes'}
<li {if $_routes[1] eq 'voucher'}class="active" {/if}><a
<li {if $_routes[1] eq 'voucher' }class="active" {/if}><a
href="{$_url}plan/voucher">{Lang::T('Vouchers')}</a></li>
<li {if $_routes[1] eq 'refill'}class="active" {/if}><a
<li {if $_routes[1] eq 'refill' }class="active" {/if}><a
href="{$_url}plan/refill">{Lang::T('Refill Customer')}</a></li>
{/if}
<li {if $_routes[1] eq 'recharge'}class="active" {/if}><a
<li {if $_routes[1] eq 'recharge' }class="active" {/if}><a
href="{$_url}plan/recharge">{Lang::T('Recharge Customer')}</a></li>
{if $_c['enable_balance'] == 'yes'}
<li {if $_routes[1] eq 'deposit'}class="active" {/if}><a
<li {if $_routes[1] eq 'deposit' }class="active" {/if}><a
href="{$_url}plan/deposit">{Lang::T('Refill Balance')}</a></li>
{/if}
{$_MENU_SERVICES}
@ -201,14 +201,14 @@
</span>
</a>
<ul class="treeview-menu">
<li {if $_routes[1] eq 'hotspot'}class="active" {/if}><a
<li {if $_routes[1] eq 'hotspot' }class="active" {/if}><a
href="{$_url}services/hotspot">{Lang::T('Hotspot')}</a></li>
<li {if $_routes[1] eq 'pppoe'}class="active" {/if}><a
<li {if $_routes[1] eq 'pppoe' }class="active" {/if}><a
href="{$_url}services/pppoe">{Lang::T('PPPOE')}</a></li>
<li {if $_routes[1] eq 'list'}class="active" {/if}><a
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
href="{$_url}bandwidth/list">{Lang::T('Bandwidth')}</a></li>
{if $_c['enable_balance'] == 'yes'}
<li {if $_routes[1] eq 'balance'}class="active" {/if}><a
<li {if $_routes[1] eq 'balance' }class="active" {/if}><a
href="{$_url}services/balance">{Lang::T('Balance')}</a></li>
{/if}
{$_MENU_PLANS}
@ -224,16 +224,32 @@
</span>
</a>
<ul class="treeview-menu">
<li {if $_routes[1] eq 'daily-report'}class="active" {/if}><a
<li {if $_routes[1] eq 'daily-report' }class="active" {/if}><a
href="{$_url}reports/daily-report">{Lang::T('Daily Reports')}</a></li>
<li {if $_routes[1] eq 'by-period'}class="active" {/if}><a
<li {if $_routes[1] eq 'by-period' }class="active" {/if}><a
href="{$_url}reports/by-period">{Lang::T('Period Reports')}</a></li>
<li {if $_routes[1] eq 'activation'}class="active" {/if}><a
<li {if $_routes[1] eq 'activation' }class="active" {/if}><a
href="{$_url}reports/activation">{Lang::T('Activation History')}</a></li>
{$_MENU_REPORTS}
</ul>
</li>
{$_MENU_AFTER_REPORTS}
<li class="{if $_system_menu eq 'message'}active{/if} treeview">
<a href="#">
<i class="ion ion-android-chat"></i> <span>{Lang::T('Send Message')}</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li {if $_routes[1] eq 'send' }class="active" {/if}><a
href="{$_url}message/send">{Lang::T('Send SMS/WA Message')}</a></li>
<li {if $_routes[1] eq 'send_bulk' }class="active" {/if}><a
href="{$_url}message/send_bulk">{Lang::T('Send Bulk SMS/WA Message')}</a></li>
{$_MENU_MESSAGE}
</ul>
</li>
{$_MENU_AFTER_MESSAGE}
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
<li class="{if $_system_menu eq 'network'}active{/if} treeview">
<a href="#">
@ -243,9 +259,9 @@
</span>
</a>
<ul class="treeview-menu">
<li {if $_routes[0] eq 'routers' and $_routes[1] eq 'list'}class="active" {/if}><a
<li {if $_routes[0] eq 'routers' and $_routes[1] eq 'list' }class="active" {/if}><a
href="{$_url}routers/list">{Lang::T('Routers')}</a></li>
<li {if $_routes[0] eq 'pool' and $_routes[1] eq 'list'}class="active" {/if}><a
<li {if $_routes[0] eq 'pool' and $_routes[1] eq 'list' }class="active" {/if}><a
href="{$_url}pool/list">{Lang::T('IP Pool')}</a></li>
{$_MENU_NETWORK}
</ul>
@ -260,7 +276,7 @@
</span>
</a>
<ul class="treeview-menu">
<li {if $_routes[0] eq 'radius' and $_routes[1] eq 'nas-list'}class="active" {/if}><a
<li {if $_routes[0] eq 'radius' and $_routes[1] eq 'nas-list' }class="active" {/if}><a
href="{$_url}radius/nas-list">{Lang::T('Radius NAS')}</a></li>
{$_MENU_RADIUS}
</ul>
@ -275,20 +291,20 @@
</span>
</a>
<ul class="treeview-menu">
<li {if $_routes[1] eq 'Order_Voucher'}class="active" {/if}><a
<li {if $_routes[1] eq 'Order_Voucher' }class="active" {/if}><a
href="{$_url}pages/Order_Voucher">{Lang::T('Order Voucher')}</a></li>
<li {if $_routes[1] eq 'Voucher'}class="active" {/if}><a
<li {if $_routes[1] eq 'Voucher' }class="active" {/if}><a
href="{$_url}pages/Voucher">{Lang::T('Voucher')} Template</a></li>
<li {if $_routes[1] eq 'Announcement'}class="active" {/if}><a
<li {if $_routes[1] eq 'Announcement' }class="active" {/if}><a
href="{$_url}pages/Announcement">{Lang::T('Announcement')}</a></li>
<li {if $_routes[1] eq 'Announcement_Customer'}class="active" {/if}><a
<li {if $_routes[1] eq 'Announcement_Customer' }class="active" {/if}><a
href="{$_url}pages/Announcement_Customer">{Lang::T('Customer Announcement')}</a>
</li>
<li {if $_routes[1] eq 'Registration_Info'}class="active" {/if}><a
<li {if $_routes[1] eq 'Registration_Info' }class="active" {/if}><a
href="{$_url}pages/Registration_Info">{Lang::T('Registration Info')}</a></li>
<li {if $_routes[1] eq 'Privacy_Policy'}class="active" {/if}><a
<li {if $_routes[1] eq 'Privacy_Policy' }class="active" {/if}><a
href="{$_url}pages/Privacy_Policy">Privacy Policy</a></li>
<li {if $_routes[1] eq 'Terms_and_Conditions'}class="active" {/if}><a
<li {if $_routes[1] eq 'Terms_and_Conditions' }class="active" {/if}><a
href="{$_url}pages/Terms_and_Conditions">Terms and Conditions</a></li>
{$_MENU_PAGES}
</ul>
@ -305,31 +321,31 @@
</a>
<ul class="treeview-menu">
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
<li {if $_routes[1] eq 'app'}class="active" {/if}><a
<li {if $_routes[1] eq 'app' }class="active" {/if}><a
href="{$_url}settings/app">{Lang::T('General Settings')}</a></li>
<li {if $_routes[1] eq 'localisation'}class="active" {/if}><a
<li {if $_routes[1] eq 'localisation' }class="active" {/if}><a
href="{$_url}settings/localisation">{Lang::T('Localisation')}</a></li>
<li {if $_routes[1] eq 'notifications'}class="active" {/if}><a
<li {if $_routes[1] eq 'notifications' }class="active" {/if}><a
href="{$_url}settings/notifications">{Lang::T('User Notification')}</a></li>
{/if}
{if in_array($_admin['user_type'],['SuperAdmin','Admin','Agent'])}
<li {if $_routes[1] eq 'users'}class="active" {/if}><a
<li {if $_routes[1] eq 'users' }class="active" {/if}><a
href="{$_url}settings/users">{Lang::T('Administrator Users')}</a></li>
{/if}
{if in_array($_admin['user_type'],['SuperAdmin','Admin'])}
<li {if $_routes[1] eq 'dbstatus'}class="active" {/if}><a
<li {if $_routes[1] eq 'dbstatus' }class="active" {/if}><a
href="{$_url}settings/dbstatus">{Lang::T('Backup/Restore')}</a></li>
<li {if $_system_menu eq 'paymentgateway'}class="active" {/if}>
<li {if $_system_menu eq 'paymentgateway' }class="active" {/if}>
<a href="{$_url}paymentgateway">
<span class="text">{Lang::T('Payment Gateway')}</span>
</a>
</li>
{$_MENU_SETTINGS}
<li {if $_routes[0] eq 'pluginmanager'}class="active" {/if}>
<li {if $_routes[0] eq 'pluginmanager' }class="active" {/if}>
<a href="{$_url}pluginmanager"><i class="glyphicon glyphicon-tasks"></i>
{Lang::T('Plugin Manager')} <small class="label pull-right">Free</small></a>
</li>
{* <li {if $_routes[0] eq 'codecanyon'}class="active" {/if}>
{* <li {if $_routes[0] eq 'codecanyon' }class="active" {/if}>
<a href="{$_url}codecanyon"><i class="glyphicon glyphicon-shopping-cart"></i>
Codecanyon.net <small class="label pull-right">Paid</small></a>
</li> *}
@ -346,10 +362,11 @@
</span>
</a>
<ul class="treeview-menu">
<li {if $_routes[1] eq 'list'}class="active" {/if}><a
<li {if $_routes[1] eq 'list' }class="active" {/if}><a
href="{$_url}logs/phpnuxbill">PhpNuxBill</a></li>
{if $_c['radius_enable']}
<li {if $_routes[1] eq 'radius'}class="active" {/if}><a href="{$_url}logs/radius">Radius</a>
<li {if $_routes[1] eq 'radius' }class="active" {/if}><a
href="{$_url}logs/radius">Radius</a>
</li>
{/if}
</ul>
@ -357,7 +374,7 @@
</li>
{/if}
{$_MENU_AFTER_LOGS}
<li {if $_system_menu eq 'community'}class="active" {/if}>
<li {if $_system_menu eq 'community' }class="active" {/if}>
<a href="{$_url}community">
<i class="ion ion-chatboxes"></i>
<span class="text">{Lang::T('Community')}</span>
@ -393,4 +410,4 @@
}
});
</script>
{/if}
{/if}