Skip to content

Instantly share code, notes, and snippets.

@bcat95
Last active March 18, 2020 02:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bcat95/4236f6d81d3cbddb12fd8c65166330e5 to your computer and use it in GitHub Desktop.
Save bcat95/4236f6d81d3cbddb12fd8c65166330e5 to your computer and use it in GitHub Desktop.
<?php
require $_SERVER['DOCUMENT_ROOT']."/admin/include/conn.php";
require $_SERVER['DOCUMENT_ROOT']."/admin/push/push-func.php";
require_once $_SERVER['DOCUMENT_ROOT'].'/vendor/autoload.php';
require_once $_SERVER['DOCUMENT_ROOT'].'/push/PushNotification.php';
// lay danh sach notification chua gui
function get_schedule_notification(){
global $connect;
$query="SELECT * FROM lwb_notifications_schedule t1 WHERE status!=2 ORDER BY schedule_id ASC LIMIT 1";
$result=mysqli_query($connect,$query);// or die(mysqli_error($connect));
if ($result){
$row = $result->fetch_assoc();
return $row;
} else return false;
}
// update danh sach notification chua gui
function set_schedule_notification($notification_id,$endpoint_start,$endpoint_end){
global $connect;
$status = ' , status=1 ';
if ($endpoint_start >= ($endpoint_end-1)) $status= ' , status=2 ';
$query="UPDATE lwb_notifications_schedule SET endpoint_start='".$endpoint_start."' ".$status." WHERE notification_id = '".$notification_id."'";
if ($connect->query($query) === TRUE) {
} else {
echo "Error: " . $query . "<br/>" . $connect->error;
}
}
// lay danh sach endpoint can gui
function get_schedule_endpoint_list($endpoint_start,$endpoint_end){
global $connect;
$query="SELECT endpoint_id FROM lwb_user_push_subscription t1 WHERE subscribed = 1 AND endpoint_id >= '".$endpoint_start."' AND endpoint_id <= '".$endpoint_end."' ORDER BY endpoint_id LIMIT 250";
$result=mysqli_query($connect,$query);// or die(mysqli_error($connect));
if ($result){
$rows = array();
while($row = $result->fetch_assoc()) {
$rows[] = $row['endpoint_id'];
}
return $rows;
} else return false;
}
// gửi nội dung và danh sách đến hàm gửi
function send_notifications($data,$arr_endpoint_id){
// $actions = array();
// foreach ($data['action_id'] as $key => $value) {
// if ($value != '') {
// $action = array();
// $action['action'] = $data['action_id'][$key];
// $action['title'] = $data['action_value'][$key];
// $action['icon'] = $data['action_icon'][$key];
// $action['url'] = $data['action_url'][$key];
// $actions[$key] = $action;
// }
// }
$sendList = array_chunk($arr_endpoint_id, 100, true);
$data['title'] = $data['headings'];
$data['body'] = $data['contents'];
$data['url'] = $data['url'];
$actions = json_decode($data['actions'], TRUE);
foreach ($sendList as $key => $arr_endpoint_id) {
$pushNotification = new PushNotification($arr_endpoint_id);
$pushNotification->setNotificationId($data['notification_id']);
if (sizeof($actions) > 0) {
$pushNotification->setActions($actions);
}
if ($data['title'] != '' && $data['body'] != '' && $data['url'] != ''){
$pushNotification->setNotificationContent($data['title'], $data['body'], $data['url']);
}
if ($data['icon'] != '' && $data['badge'] != ''){
$pushNotification->setIconAndBadge($data['icon'], $data['badge']);
}
if ($data['image'] != ''){
$pushNotification->setImage($data['image']);
}
try {
$pushNotification->sendNotifications();
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n<br/>";
continue;
}
}
// sleep(3);
return TRUE;
}
// kiem tra va gui
function schedule_main(){
$milliseconds = round(microtime(true) * 1000000);
$notification = get_schedule_notification();
if (!$notification){
echo "notification schedule null<br/>";
return false;
} else {
$endpoint_start = $notification['endpoint_start'];
$endpoint_end = $notification['endpoint_end'];
$arr_endpoint_id = get_schedule_endpoint_list($endpoint_start,$endpoint_end);
if ($arr_endpoint_id) {
$endpoint_start = $arr_endpoint_id[sizeof($arr_endpoint_id)-1];
$data = get_notification($notification['notification_id']);
if (!$data) {
echo "notification data null<br/>";
return false;
} else {
if (send_notifications($data,$arr_endpoint_id))
set_schedule_notification($notification['notification_id'],$endpoint_start,$endpoint_end);
else set_schedule_notification($notification['notification_id'],$endpoint_start,$endpoint_end);
}
} else echo "arr_endpoint_id null<br/>";
}
$milliseconds=round(microtime(true) * 1000000)-$milliseconds;
echo "{'success': [{'message': 'Kiểm tra Log ".($milliseconds/1000000)."s'}]}<br/>";
}
while(get_schedule_notification()) {
schedule_main();
}
echo '{"success":[{"message":"notification schedule null"}]}';
?>
<?php
use Minishlink\WebPush\Encryption;
use Minishlink\WebPush\Subscription;
use Minishlink\WebPush\WebPush;
class PushNotification
{
/**
* @var User
*/
protected $receiver;
/**
* @var array
*/
protected $subscriptions;
/**
* @var array
*/
protected $payloadData = [];
public function __construct($receiver)
{
$this->receiver = $receiver;
}
public function logError($messages){
echo $messages;
}
public function setNotificationId($notification_id)
{
$this->payloadData['notification_id'] = $notification_id;
}
public function setNotificationContent($title, $body, $url = null)
{
$this->payloadData['title'] = $title;
$this->payloadData['body'] = $body;
$this->payloadData['url'] = $url;
}
public function setImage($image)
{
$this->payloadData['image'] = $image;
}
public function setIconAndBadge($icon, $badge = null)
{
$this->payloadData['icon'] = $icon;
if ($badge)
{
$this->payloadData['badge'] = $badge;
}
}
public function setDirection($direction)
{
$this->payloadData['dir'] = $direction;
}
public function setNotificationTag($tag)
{
$this->payloadData['tag'] = $tag;
}
public function setActions($actions)
{
$actions = json_encode($actions,JSON_UNESCAPED_UNICODE);
$this->payloadData['actions'] = $actions;
}
public function setCustomPayloadData($name, $value)
{
$this->payloadData[$name] = $value;
}
protected function applyPayloadDefaults()
{
$this->payloadData = array_replace([
'title' => 'Thông báo từ ABCDEF',
'body' => 'Bạn có thông báo mới từ ABCDEF',
'url' => 'https://ABCDEF.com/?utm_source=push',
'image' => $this->getDefaultImageForVisitor(),
'badge' => $this->getDefaultBadgeForVisitor(),
'icon' => $this->getDefaultIconForVisitor(),
'subscriber_id' => '',
'notification_id' => '',
'dir' => 'ltr',
'tag' => '',
'tag_phrase' => 'plus_x_previous',
'actions' => $this->getDefaultActionsForVisitor(),
], $this->payloadData);
return $this->payloadData;
}
public function isPushAvailable()
{
return true;
}
public function isReceiverSubscribed()
{
$subscriptions = $this->getReceiverSubscriptions();
return boolval(count($subscriptions));
}
protected function getReceiverSubscriptions()
{
if ($this->subscriptions === null)
{
$this->subscriptions = $this->getUserSubscriptions($this->receiver);
}
return $this->subscriptions;
}
public function sendNotifications()
{
if (!$this->isPushAvailable())
{
return;
}
$payload = $this->applyPayloadDefaults();
$webPush = $this->getWebPushObject();
$webPush->setDefaultOptions([
'TTL' => 86400, // if undelivered after 1 day, expire the notification
]);
$subscriptions = $this->getReceiverSubscriptions();
foreach ($subscriptions AS $subscription)
{
$authData = json_decode($subscription['data'], true);
$payload['subscriber_id'] = $subscription['endpoint_id'];
$array = [
'endpoint' => $subscription['endpoint'],
'publicKey' => $authData['key'],
'authToken' => $authData['token'],
'contentEncoding' => $authData['encoding']
];
try
{
$webPush->setAutomaticPadding($this->getEndpointPadding($subscription['endpoint']));
$subObj = Subscription::create($array);
// $subObj = Subscription::create([
// 'endpoint' => $subscription['endpoint'],
// 'publicKey' => $authData['key'],
// 'authToken' => $authData['token'],
// 'contentEncoding' => $authData['encoding']
// ]);
$webPush->sendNotification(
$subObj,
json_encode($payload)
);
}
catch (\Exception $e)
{
// generally indicates that the payload is too big which at ~3000 bytes shouldn't happen for a typical alert...
continue;
}
}
foreach ($webPush->flush() as $report) {
$results[] = $report->jsonSerialize();
}
$this->handleResults($results, $subscriptions);
}
protected function getEndpointPadding($endpoint)
{
if (strpos($endpoint, 'mozilla') !== false)
{
// firefox, at least on Android, has an issue with automatic padding which
// is used to make encryption more secure, but makes encryption slower.
// see: https://github.com/web-push-libs/web-push-php/issues/108
// TODO: ideally won't need this forever if Mozilla ever fix this or if library works-around it
return 0;
}
if (strpos($endpoint, '.ucweb.com') !== false)
{
return 0;
}
return Encryption::MAX_COMPATIBILITY_PAYLOAD_LENGTH;
}
protected function handleResults($results, array $subscriptions)
{
if (is_array($results)) // may have errors
{
global $connect;
$notification_id = $this->payloadData['notification_id'];
$remaining = 0;
$successful = 0;
$failed = 0;
$canceled = 0;
foreach ($results AS $result)
{
if ($result['success'])
{
if (isset($result['endpoint']))
{
$endpointHash = $this->getEndpointHash($result['endpoint']);
$remaining += 1;$successful += 1;
$query = "UPDATE user_push_subscription SET last_seen = '".time()."' WHERE endpoint_hash = '".$endpointHash."'";
mysqli_query($connect,$query) or die(mysqli_error($connect));
}
else
{
// Mozilla doesn't appear to send anything other than 'success' in its successful results.
continue;
}
}
else if (!empty($result['expired']))
{
switch ($result['expired'])
{
// 401 and 403 generally represent when something in the request doesn't match, likely
// due to VAPID keys changing
case 401:
case 403:
case 404:
case 410:
if (isset($result['endpoint']))
{
$endpointHash = $this->getEndpointHash($result['endpoint']);
$remaining += 1;$failed += 1;
$query = "UPDATE user_push_subscription SET subscribed = '0' WHERE endpoint_hash = '".$endpointHash."'";
mysqli_query($connect,$query) or die(mysqli_error($connect));
}
break;
case 406:
// not a server error but rate limiting - future pushes should work
case 500:
case 502:
case 503:
case 504:
// these indicate server errors that are likely temporary - future pushes should work
break;
default:
$this->logError ("Push notification failure: " . \GuzzleHttp\json_encode($result));
if (isset($result['endpoint']))
{
$endpointHash = $this->getEndpointHash($result['endpoint']);
$remaining += 1;$failed += 1;$canceled+=1;
// $query = "UPDATE user_push_subscription SET subscribed = '0' WHERE endpoint_hash = '".$endpointHash."'";
// mysqli_query($connect,$query) or die(mysqli_error($connect));
}
break;
}
}
else
{
$this->logError("Push notification failure: " . \GuzzleHttp\json_encode($result));
if (isset($result['endpoint']))
{
$endpointHash = $this->getEndpointHash($result['endpoint']);
// $query = "UPDATE user_push_subscription SET subscribed = '0' WHERE endpoint_hash = '".$endpointHash."'";
// mysqli_query($connect,$query) or die(mysqli_error($connect));
}
$remaining += 1;$failed += 1;$canceled+=1;
}
}
// update notification data
$query = "UPDATE push_notifications SET completed_at = '".time()."',canceled = canceled+".$canceled.", failed = failed+".$failed.", successful = successful+".$successful.", remaining = remaining-".$remaining." WHERE notification_id = '".$notification_id."'";
mysqli_query($connect,$query) or die(mysqli_error($connect));
}
else // no errors so mark all as seen
{
// $endpointHashes = array_map(
// [$pushRepo, 'getEndpointHash'],
// array_column($subscriptions, 'endpoint')
// );
// $db->update('xf_user_push_subscription', [
// 'last_seen' => time()
// ], 'endpoint_hash IN(' . $db->quote($endpointHashes) . ')');
}
}
/**
* @return WebPush
*/
protected function getWebPushObject()
{
$auth = array(
'VAPID' => array(
'subject' => 'https://ABCDEF.com/', // NBH Key
'publicKey' => 'ABCDEF', // don't forget that your public key also lives in app.js
'privateKey' => 'ABCDEF' // in the real world, this would be in a secret file
)
);
$clientOptions = [
\GuzzleHttp\RequestOptions::ALLOW_REDIRECTS => false,
]; // see \GuzzleHttp\RequestOptions
return new WebPush($auth, [], 10, $clientOptions);
}
protected function getDefaultImageForVisitor()
{
$image = '';
return $image;
}
protected function getDefaultBadgeForVisitor()
{
$badge = 'hhttps://ABCDEF.com/icon-96x96.png';
return $badge;
}
protected function getDefaultIconForVisitor()
{
$icon = 'https://ABCDEF.com/main/styles/s-logo.png';
return $icon;
}
protected function getDefaultActionsForVisitor()
{
$actions = '[
{ "action": "close", "title": "Đóng"}
]';
$actions = json_decode($actions, TRUE);
return json_encode($actions);
}
protected function getUserSubscriptions($receiver)
{
$subscriptions = array();
foreach ($receiver as $key => $value) {
if (get_endpoint($value)) $subscriptions[$key] = get_endpoint($value);
}
return $subscriptions;
}
protected function getEndpointHash($endpoint)
{
return md5($endpoint);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment