main->title . ' (' . SITE_URL . '): Provided type not allowed.'); } $pixel_key = isset($this->params[0]) ? input_clean($this->params[0]) : null; /* Get the details of the website from the database */ $website = (new \Altum\Models\Website())->get_website_by_pixel_key($pixel_key); /* Make sure the website has access */ if(!$website) { die(settings()->main->title . ' (' . SITE_URL . '): No website found for this pixel.'); } if(!$website->is_enabled) { die(settings()->main->title . ' (' . SITE_URL . '): Website disabled.'); } /* Make sure to get the user data and confirm the user is ok */ $user = (new \Altum\Models\User())->get_user_by_user_id($website->user_id); if(!$user) { die("console.log('" . settings()->main->title . " (" . SITE_URL. "): Website owner not found.')"); } if($user->status != 1) { die("console.log('" . settings()->main->title . " (" . SITE_URL. "): Website owner is disabled.')"); } /* Check for a custom domain */ if(isset(\Altum\Router::$data['domain']) && $website->domain_id != \Altum\Router::$data['domain']->domain_id) { die("console.log('" . settings()->main->title . " (" . SITE_URL. "): Domain id mismatch.')"); } /* Process the plan of the user */ (new User())->process_user_plan_expiration_by_user($user); /* Create and Delete handlers */ if(in_array($_POST['type'], ['create', 'delete'])) { /* Check for any errors */ $required_fields = ['url', 'endpoint', 'p256dh', 'auth']; foreach($required_fields as $field) { if(!isset($_POST[$field]) || (isset($_POST[$field]) && empty($_POST[$field]) && $_POST[$field] != '0')) { redirect(); } } /* Parse the data */ $_POST['endpoint'] = get_url($_POST['endpoint']); $_POST['url'] = parse_url($_POST['url'], PHP_URL_HOST) == $website->host ? input_clean($_POST['url'], 2048) : null; $unique_endpoint_id = md5($_POST['endpoint']); $keys = json_encode([ 'p256dh' => $_POST['p256dh'], 'auth' => $_POST['auth'], ]); /* Make sure only whitelisted endpoints are accepted */ $endpoint = parse_url($_POST['endpoint']); $whitelisted_hosts = [ 'android.googleapis.com', 'fcm.googleapis.com', 'updates.push.services.mozilla.com', 'updates-autopush.stage.mozaws.net', 'updates-autopush.dev.mozaws.net', 'notify.windows.com', 'push.apple.com', ]; $accepted = false; foreach($whitelisted_hosts as $whitelisted_host) { if(string_ends_with($whitelisted_host, $endpoint['host'])) { $accepted = true; } } if(!$accepted) { die("console.log('" . settings()->main->title . " (" . SITE_URL . "): Endpoint not allowed.')"); } } $ip = get_ip(); $original_ip = $ip; /* Check if we can save the real IP or not */ $ip = $website->settings->ip_storage_is_enabled ? $ip : preg_replace('/\d/', '*', $ip); switch($_POST['type']) { case 'create': /* Check for the plan limit */ $websites = (new \Altum\Models\Website())->get_websites_by_user_id($user->user_id); $total_subscribers = 0; foreach($websites as $row) { $total_subscribers += $row->total_subscribers; } if($user->plan_settings->subscribers_limit != -1 && $total_subscribers >= $user->plan_settings->subscribers_limit) { die("console.log('" . settings()->main->title . " (" . SITE_URL . "): Subscribers limit reached.')"); } /* Detect the location */ try { $maxmind = (new Reader(APP_PATH . 'includes/GeoLite2-City.mmdb'))->get($original_ip); } catch(\Exception $exception) { /* :) */ } $continent_code = isset($maxmind) && isset($maxmind['continent']) ? $maxmind['continent']['code'] : null; $country_code = isset($maxmind) && isset($maxmind['country']) ? $maxmind['country']['iso_code'] : null; $city_name = isset($maxmind) && isset($maxmind['city']) ? $maxmind['city']['names']['en'] : null; /* Detect extra details about the user */ $whichbrowser = new \WhichBrowser\Parser($_SERVER['HTTP_USER_AGENT']); $browser_name = $whichbrowser->browser->name ?? null; $os_name = $whichbrowser->os->name ?? null; $browser_language = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? mb_substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) : null; $device_type = get_device_type($_SERVER['HTTP_USER_AGENT']); /* Check for custom parameters */ $custom_parameters = []; if(isset($_POST['custom_parameters'])) { $i = 1; foreach((array) $_POST['custom_parameters'] as $key => $value) { $key = input_clean($key, '64'); $value = input_clean($value, '512'); if($i++ >= 10) { break; } else { $custom_parameters[$key] = $value; } } } $custom_parameters = json_encode($custom_parameters); /* Insert / update */ $subscriber_id = db()->onDuplicate([ 'endpoint', 'keys', ])->insert('subscribers', [ 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'unique_endpoint_id' => $unique_endpoint_id, 'endpoint' => $_POST['endpoint'], 'keys' => $keys, 'ip' => $ip, 'custom_parameters' => $custom_parameters, 'city_name' => $city_name, 'country_code' => $country_code, 'continent_code' => $continent_code, 'os_name' => $os_name, 'browser_name' => $browser_name, 'browser_language' => $browser_language, 'device_type' => $device_type, 'subscribed_on_url' => $_POST['url'], 'datetime' => get_date(), ]); /* Update website statistics */ if(db()->count == 1) { db()->where('website_id', $website->website_id)->update('websites', ['total_subscribers' => db()->inc()]); /* Clear the cache */ cache()->deleteItem('subscribers_total?user_id=' . $website->user_id); cache()->deleteItem('subscribers_dashboard?user_id=' . $website->user_id); /* Processing the notification handlers */ if(count($website->notifications ?? [])) { $notification_handlers = (new \Altum\Models\NotificationHandlers())->get_notification_handlers_by_user_id($user->user_id); /* Processing the notification handlers */ foreach($notification_handlers as $notification_handler) { if(!$notification_handler->is_enabled) continue; if(!in_array($notification_handler->notification_handler_id, $website->notifications)) continue; $subscriber_data = [ 'website_id' => $website->website_id, 'url' => url('subscriber/' . $subscriber_id), 'ip' => $ip, 'subscribed_on_url' => $_POST['url'], 'city_name' => $city_name, 'country_code' => $country_code, 'continent_code' => $continent_code, 'os_name' => $os_name, 'browser_name' => $browser_name, 'browser_language' => $browser_language, 'device_type' => $device_type, ]; switch($notification_handler->type) { case 'email': /* Prepare the html for the email body */ $email_body = ''; $email_template = get_email_template( [ '{{WEBSITE_NAME}}' => $website->name, ], l('global.emails.user_new_subscriber.subject', $user->language), [ '{{SUBSCRIBER_IP}}' => $ip, '{{WEBSITE_NAME}}' => $website->name, '{{WEBSITE_URL}}' => $website->scheme . $website->host . $website->path, '{{DATA}}' => $email_body ], l('global.emails.user_new_subscriber.body', $user->language), ); /* Send the email */ send_mail($notification_handler->settings->email, $email_template->subject, $email_template->body, ['anti_phishing_code' => $user->anti_phishing_code, 'language' => $user->language]); break; case 'webhook': try { \Unirest\Request::post($notification_handler->settings->webhook, [], $subscriber_data); } catch (\Exception $exception) { error_log($exception->getMessage()); } break; case 'slack': $caught_data = "\r\n\r\n"; foreach($subscriber_data as $key => $value) { $caught_data .= '*' . $key . '*: ' .$value . "\r\n"; } $caught_data .= "\r\n\r\n"; try { \Unirest\Request::post( $notification_handler->settings->slack, ['Accept' => 'application/json'], \Unirest\Request\Body::json([ 'text' => sprintf( l('websites.simple_notification', $user->language), $website->name, $website->scheme . $website->host . $website->path, $caught_data, url('subscriber/' . $subscriber_id) ), 'username' => settings()->main->title, 'icon_emoji' => ':large_green_circle:' ]) ); } catch (\Exception $exception) { error_log($exception->getMessage()); } break; case 'discord': $caught_data = "\r\n\r\n"; foreach($subscriber_data as $key => $value) { $caught_data .= '*' . $key . '*: ' .$value . "\r\n"; } $caught_data .= "\r\n\r\n"; try { \Unirest\Request::post( $notification_handler->settings->discord, [ 'Accept' => 'application/json', 'Content-Type' => 'application/json', ], \Unirest\Request\Body::json([ 'embeds' => [ [ 'title' => sprintf( l('websites.simple_notification', $user->language), $website->name, $website->name, $caught_data, url('subscriber/' . $subscriber_id) ), 'color' => '2664261', ] ], ]) ); } catch (\Exception $exception) { error_log($exception->getMessage()); } break; case 'telegram': $caught_data = urlencode("\r\n\r\n"); foreach($subscriber_data as $key => $value) { $caught_data .= '' . $key . ': ' . $value . urlencode("\r\n"); } $caught_data .= urlencode("\r\n\r\n"); try { $test = \Unirest\Request::get( sprintf( 'https://api.telegram.org/bot%s/sendMessage?chat_id=%s&text=%s&parse_mode=html', $notification_handler->settings->telegram, $notification_handler->settings->telegram_chat_id, sprintf( l('websites.simple_notification', $user->language), $website->name, $website->scheme . $website->host . $website->path, $caught_data, url('subscriber/' . $subscriber_id) ) ) ); } catch (\Exception $exception) { error_log($exception->getMessage()); } break; case 'microsoft_teams': $caught_data = "\r\n\r\n"; foreach($subscriber_data as $key => $value) { $caught_data .= $key . ': ' .$value . "\r\n"; } $caught_data .= "\r\n\r\n"; try { \Unirest\Request::post( $notification_handler->settings->microsoft_teams, ['Content-Type' => 'application/json'], \Unirest\Request\Body::json([ 'text' => sprintf( l('websites.simple_notification', $user->language), $website->name, $website->scheme . $website->host . $website->path, $caught_data, url('subscriber/' . $subscriber_id) ), ]) ); } catch (\Exception $exception) { error_log($exception->getMessage()); } break; case 'x': $caught_data = "\r\n\r\n"; foreach($subscriber_data as $key => $value) { $caught_data .= $key . ': ' .$value . "\r\n"; } $caught_data .= "\r\n\r\n"; $twitter = new \Abraham\TwitterOAuth\TwitterOAuth( $notification_handler->settings->x_consumer_key, $notification_handler->settings->x_consumer_secret, $notification_handler->settings->x_access_token, $notification_handler->settings->x_access_token_secret ); $twitter->setApiVersion('2'); try { $response = $twitter->post('tweets', ['text' => sprintf( l('websites.simple_notification', $user->language), $website->name, $website->scheme . $website->host . $website->path, $caught_data, url('subscriber/' . $subscriber_id) )]); } catch (\Exception $exception) { /* :* */ } break; case 'twilio': try { \Unirest\Request::auth(settings()->notification_handlers->twilio_sid, settings()->notification_handlers->twilio_token); \Unirest\Request::post( sprintf('https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json', settings()->notification_handlers->twilio_sid), [], [ 'From' => settings()->notification_handlers->twilio_number, 'To' => $notification_handler->settings->twilio, 'Body' => sprintf( l('websites.simple_notification', $user->language), $website->name, $website->scheme . $website->host . $website->path, "\r\n\r\n", url('subscriber/' . $subscriber_id) ), ] ); } catch (\Exception $exception) { error_log($exception->getMessage()); } \Unirest\Request::auth('', ''); break; case 'twilio_call': try { \Unirest\Request::auth(settings()->notification_handlers->twilio_sid, settings()->notification_handlers->twilio_token); \Unirest\Request::post( sprintf('https://api.twilio.com/2010-04-01/Accounts/%s/Calls.json', settings()->notification_handlers->twilio_sid), [], [ 'From' => settings()->notification_handlers->twilio_number, 'To' => $notification_handler->settings->twilio_call, 'Url' => SITE_URL . 'twiml/notification.simple_notification?param1=' . urlencode($website->name) . '¶m2=' . urlencode($website->scheme . $website->host . $website->path) . '¶m3=¶m4=' . urlencode(url('subscriber/' . $subscriber_id)), ] ); } catch (\Exception $exception) { error_log($exception->getMessage()); } \Unirest\Request::auth('', ''); break; case 'whatsapp': try { $test = \Unirest\Request::post( 'https://graph.facebook.com/v18.0/' . settings()->notification_handlers->whatsapp_number_id . '/messages', [ 'Authorization' => 'Bearer ' . settings()->notification_handlers->whatsapp_access_token, 'Content-Type' => 'application/json' ], \Unirest\Request\Body::json([ 'messaging_product' => 'whatsapp', 'to' => $notification_handler->settings->whatsapp, 'type' => 'template', 'template' => [ 'name' => 'new_subscriber', 'language' => [ 'code' => \Altum\Language::$default_code ], 'components' => [[ 'type' => 'body', 'parameters' => [ [ 'type' => 'text', 'text' => $website->name ], [ 'type' => 'text', 'text' => $website->scheme . $website->host . $website->path ], [ 'type' => 'text', 'text' => url('subscriber/' . $subscriber_id) ], ] ]] ] ]) ); } catch (\Exception $exception) { error_log($exception->getMessage()); } break; case 'push_subscriber_id': $push_subscriber = db()->where('push_subscriber_id', $notification_handler->settings->push_subscriber_id)->getOne('push_subscribers'); if(!$push_subscriber) { db()->where('notification_handler_id', $notification_handler->notification_handler_id)->update('notification_handlers', ['is_enabled' => 0]); }; /* Prepare the web push */ $push_notification = \Altum\Helpers\PushNotifications::send([ 'title' => l('websites.push_notification.title', $user->language), 'description' => sprintf(l('websites.push_notification.description', $user->language), $website->name, $website->scheme . $website->host . $website->path), 'url' => url('subscriber/' . $subscriber_id), ], $push_subscriber); /* Unsubscribe if push failed */ if(!$push_notification) { db()->where('push_subscriber_id', $push_subscriber->push_subscriber_id)->delete('push_subscribers'); db()->where('notification_handler_id', $notification_handler->notification_handler_id)->update('notification_handlers', ['is_enabled' => 0]); } break; } } } } /* Update/resub on an already subscribed user */ else { /* Insert subscriber log */ db()->insert('subscribers_logs', [ 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'type' => 'unsubscribed', 'ip' => $ip, 'datetime' => get_date(), ]); } /* Insert subscriber log */ db()->insert('subscribers_logs', [ 'subscriber_id' => $subscriber_id, 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'ip' => $ip, 'type' => 'subscribed', 'datetime' => get_date(), ]); /* Check for potential flows */ $flows = (new \Altum\Models\Flow())->get_flows_by_website_id($website->website_id); /* Go through each flow and set up the scheduled notifications */ foreach($flows as $flow) { if(!$flow->is_enabled) continue; /* Make sure the subscriber triggers the selected segment */ $flow_is_triggered = false; /* Segment */ if(is_numeric($flow->segment)) { /* Get settings from custom segments */ $segment = (new \Altum\Models\Segment())->get_segment_by_segment_id($flow->segment); if(!$segment) { $flow->segment = 'all'; } } switch($flow->segment) { case 'all': $flow_is_triggered = true; break; default: /* Assume the flow is triggered */ $flow_is_triggered = true; if(count($segment->settings->filters_countries) && !in_array($country_code, $segment->settings->filters_countries)) { $flow_is_triggered = false; } if(count($segment->settings->filters_continents) && !in_array($continent_code, $segment->settings->filters_continents)) { $flow_is_triggered = false; } if(count($segment->settings->filters_device_type) && !in_array($device_type, $segment->settings->filters_device_type)) { $flow_is_triggered = false; } if(count($segment->settings->filters_device_type) && !in_array($device_type, $segment->settings->filters_device_type)) { $flow_is_triggered = false; } if(count($segment->settings->filters_languages) && !in_array($browser_language, $segment->settings->filters_languages)) { $flow_is_triggered = false; } if(count($segment->settings->filters_operating_systems) && !in_array($os_name, $segment->settings->filters_operating_systems)) { $flow_is_triggered = false; } if(count($segment->settings->filters_browsers) && !in_array($browser_name, $segment->settings->filters_browsers)) { $flow_is_triggered = false; } break; } /* Ignore if it's not triggered */ if(!$flow_is_triggered) continue; /* Scheduled date */ $scheduled_datetime = (new \DateTime())->modify('+' . $flow->wait_time . ' ' . $flow->wait_time_type)->format('Y-m-d H:i:s'); /* Insert the scheduled the notification */ db()->insert('flow_notifications', [ 'subscriber_id' => $subscriber_id, 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'flow_id' => $flow->flow_id, 'datetime' => get_date(), 'scheduled_datetime' => $scheduled_datetime, ]); } break; case 'delete': /* Delete subscriber */ db()->where('unique_endpoint_id', $unique_endpoint_id)->delete('subscribers'); /* Update website statistics */ if(db()->count) { db()->where('website_id', $website->website_id)->update('websites', ['total_subscribers' => db()->dec()]); /* Clear the cache */ cache()->deleteItem('subscribers_total?user_id=' . $website->user_id); cache()->deleteItem('subscribers_dashboard?user_id=' . $website->user_id); } /* Insert subscriber log */ db()->insert('subscribers_logs', [ 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'type' => 'unsubscribed', 'ip' => $ip, 'datetime' => get_date(), ]); break; case 'displayed_notification': case 'clicked_notification': case 'closed_notification': /* Only track those stats if the user has the right plan settings */ if(!$user->plan_settings->analytics_is_enabled) { break; } /* Check for any errors */ $required_fields = ['subscriber_id',]; foreach($required_fields as $field) { if(!isset($_POST[$field]) || (isset($_POST[$field]) && empty($_POST[$field]) && $_POST[$field] != '0')) { redirect(); } } $subscriber_id = (int) $_POST['subscriber_id']; /* Get the subscriber */ $subscriber = (new Subscriber())->get_subscriber_by_subscriber_id($subscriber_id); /* Campaign tracking log */ if(isset($_POST['campaign_id'])) { $campaign_id = (int) $_POST['campaign_id']; /* Insert subscriber log */ db()->insert('subscribers_logs', [ 'subscriber_id' => $subscriber->subscriber_id, 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'campaign_id' => $campaign_id, 'type' => $_POST['type'], 'ip' => $ip, 'datetime' => get_date(), ]); /* More stats recording */ $stat_table_column = match ($_POST['type']) { 'displayed_notification' => 'total_displayed_push_notifications', 'clicked_notification' => 'total_clicked_push_notifications', 'closed_notification' => 'total_closed_push_notifications', }; /* Update campaign statistics */ db()->where('campaign_id', $campaign_id)->update('campaigns', [$stat_table_column => db()->inc()]); } /* Flow tracking log */ else if(isset($_POST['flow_id'])) { $flow_id = (int) $_POST['flow_id']; /* Insert subscriber log */ db()->insert('subscribers_logs', [ 'subscriber_id' => $subscriber->subscriber_id, 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'flow_id' => $flow_id, 'type' => $_POST['type'], 'ip' => $ip, 'datetime' => get_date(), ]); /* More stats recording */ $stat_table_column = match ($_POST['type']) { 'displayed_notification' => 'total_displayed_push_notifications', 'clicked_notification' => 'total_clicked_push_notifications', 'closed_notification' => 'total_closed_push_notifications', }; /* Update campaign statistics */ db()->where('flow_id', $flow_id)->update('flows', [$stat_table_column => db()->inc()]); } /* Personal notification tracking log */ else if(isset($_POST['personal_notification_id'])) { $personal_notification_id = (int) $_POST['personal_notification_id']; /* Insert subscriber log */ db()->insert('subscribers_logs', [ 'subscriber_id' => $subscriber->subscriber_id, 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'personal_notification_id' => $personal_notification_id, 'type' => $_POST['type'], 'ip' => $ip, 'datetime' => get_date(), ]); $stat_table_column = match ($_POST['type']) { 'displayed_notification' => 'is_displayed', 'clicked_notification' => 'is_clicked', 'closed_notification' => 'is_closed', }; /* Update personal notification */ db()->where('personal_notification_id', $personal_notification_id)->update('personal_notifications', [$stat_table_column => 1]); /* Stats table for the rest of updates */ $stat_table_column = match ($_POST['type']) { 'displayed_notification' => 'total_displayed_push_notifications', 'clicked_notification' => 'total_clicked_push_notifications', 'closed_notification' => 'total_closed_push_notifications', }; } /* Update subscriber statistics */ db()->where('subscriber_id', $subscriber->subscriber_id)->update('subscribers', [$stat_table_column => db()->inc()]); /* Update website statistics */ db()->where('website_id', $website->website_id)->update('websites', [$stat_table_column => db()->inc()]); break; case 'permission_denied': /* Only track those stats if the user has the right plan settings */ if(!$user->plan_settings->analytics_is_enabled) { break; } /* Insert subscriber log */ db()->insert('subscribers_logs', [ 'website_id' => $website->website_id, 'user_id' => $website->user_id, 'type' => $_POST['type'], 'ip' => $ip, 'datetime' => get_date(), ]); break; } } }