/** * Do a tracker_log incase anything went wrong with a query. */ function checkDatabaseInsert($query) { global $DBH; if (!$query) { $e_code = $DBH->errorInfo()[1]; $e_msg = $DBH->errorInfo()[2]; // Database constraint if ($e_code != 7) { tracker_log('[error] Database exception: code: ' . $e_code . ', message: ' . $e_msg); } else { tracker_debug('[assumed safe error] Database exception: code: ' . $e_code . ', message: ' . $e_msg); } } return $query; }
function handPresenceChange($number, $type, $DBH, $wa, $crawl_time, $whatsspyNotificatons) { global $whatsspyHeuristicOptions; $status = $type == 'available' ? true : false; $latest_status = $DBH->prepare('SELECT "sid", "status", ROUND(EXTRACT(\'epoch\' FROM "start")) as "start" FROM status_history WHERE "number"=:number AND "end" IS NULL'); $latest_status->execute(array(':number' => $number)); $real_time = $crawl_time; if ($latest_status->rowCount() == 0) { tracker_debug('No status records found for ' . $number . '. Inserting first one.'); // Insert new record if ($status == true) { // Once a user comes online, you will be notified by WhatsApp within 2-3 seconds. $real_time = $real_time + $whatsspyHeuristicOptions['onPresenceAvailableLag']; } $insert = $DBH->prepare('INSERT INTO status_history ("status", "start", "number", "end") VALUES (:status, :start, :number, NULL);'); if (checkDatabaseInsert($insert->execute(array(':status' => (int) $status, ':number' => $number, ':start' => date('c', $real_time))))) { tracker_log(' -[poll] ' . $number . ' is now ' . $type . '.'); if ($type == 'available') { sendNotification($DBH, $wa, $whatsspyNotificatons, 'user', ['title' => ':name status change', 'description' => ':name is now ' . $type . '.', 'number' => $number, 'notify_type' => 'status']); } } } else { tracker_debug('Status records found for ' . $number . '.'); $row = $latest_status->fetch(); # Latest status is the same as the current status : Do nothing # Latest status is different from the current status : End record and start new one if ($row['status'] != $status) { tracker_debug('Latest status (' . $row['status'] . ') is different from this one (' . $status . '). Processing update.'); if ($row['status'] == true) { // Correct ending time of this online status if ($row['start'] < $real_time + $whatsspyHeuristicOptions['onPresenceUnavailableLagFase1']) { $real_time = $real_time + $whatsspyHeuristicOptions['onPresenceUnavailableLagFase1']; } elseif ($row['start'] < $real_time + $whatsspyHeuristicOptions['onPresenceUnavailableLagFase2']) { $real_time = $real_time + $whatsspyHeuristicOptions['onPresenceUnavailableLagFase2']; } elseif ($row['start'] < $real_time + $whatsspyHeuristicOptions['onPresenceUnavailableLagFase3']) { $real_time = $real_time + $whatsspyHeuristicOptions['onPresenceUnavailableLagFase3']; } else { if ($row['start'] < $real_time) { // End time is after before time, seems ok } else { // It seems like the timing is off, assume small session of 10 seconds. $real_time = $row['start'] + 10; } } } else { // Correct starting time of this online status $real_time = $real_time + $whatsspyHeuristicOptions['onPresenceAvailableLag']; } $update = $DBH->prepare('UPDATE status_history SET "end" = :end WHERE number = :number AND sid = :sid;'); checkDatabaseInsert($update->execute(array(':number' => $number, ':sid' => $row['sid'], ':end' => date('c', $real_time)))); # Create new record $insert = $DBH->prepare('INSERT INTO status_history ( "status", "start", "number", "end") VALUES (:status, :start, :number, NULL);'); if (checkDatabaseInsert($insert->execute(array(':status' => (int) $status, ':number' => $number, ':start' => date('c', $real_time))))) { tracker_log(' -[poll] ' . $number . ' is now ' . $type . '.'); if ($type == 'available') { sendNotification($DBH, $wa, $whatsspyNotificatons, 'user', ['title' => ':name status change', 'description' => ':name is now ' . $type . '.', 'number' => $number, 'notify_type' => 'status']); } } } } }
/** * CONTINIOUS TRACKING * Tracking: * - User status changes to track if a user is online/offline * - User lastseen (privacy options) (attached to online/offline status) * - User profile pictures (and changes) * - User status message (and changes) */ function track() { global $DBH, $wa, $tracking_ticks, $tracking_numbers, $whatsspyNotificatons, $crawl_time, $whatsappAuth, $pollCount, $lastseenCount, $statusMsgCount, $picCount, $request_error_queue, $continue_tracker_session, $whatsspyPerformanceMode; $crawl_time = time(); setupWhatsappHandler(); retrieveTrackingUsers(); tracker_log('[init] Started tracking with phonenumber ' . $whatsappAuth['number']); if ($continue_tracker_session == false) { startTrackerHistory(); sendNotification($DBH, null, $whatsspyNotificatons, 'tracker', ['title' => 'WhatsSpy Public has started tracking!', 'description' => 'tracker has started tracking ' . count($tracking_numbers) . ' users.', 'event-type' => 'start']); } else { $continue_tracker_session = false; } while (true) { $crawl_time = time(); // Socket read $tick_start = microtime(true); if ($whatsspyPerformanceMode === true) { while (microtime(true) - $tick_start < 1.0 && $wa->pollMessage() === true) { echo microtime(true) - $tick_start . "\r\n"; tracker_debug('Socket read called with poll time: ' . microtime(true) - $tick_start); } } else { $wa->pollMessage(); } $tick_end = microtime(true); $poll_took = number_format($tick_end - $tick_start, 4); // Check if database set is up to date if (count($tracking_numbers) > base64_decode('NzAw')) { tracker_log(base64_decode("UEiQOiBGYXRhbCBFcnJvcjogVHJhY2tpbmcgdG9vIG1hbnkgY29udGFjdHMsIGFib3J0aW5nIHRyYWNraW5nLg=="), true, true); exit; } list($usec, $sec) = explode(' ', microtime()); // split the microtime on space with two tokens $usec and $sec. $usec = str_replace("0.", ".", number_format($usec, 4)); // remove the leading '0.' from usec tracker_log("[poll #{$pollCount}] Tracking " . count($tracking_numbers) . " users (poll took {$poll_took})", true, false); // 1) STATUS MESSAGE (and privacy) // // Check status message if ($pollCount % calculateTick($tracking_ticks['statusmsg']) == 0) { tracker_log('[status-msg #' . $statusMsgCount . '] Checking ' . count($tracking_numbers) . ' users.'); if (count($tracking_numbers) > 0) { $wa->sendGetStatuses($tracking_numbers); } $statusMsgCount++; } // 2) PROFILE PICTURE (and privacy) // // Check profile picture if ($pollCount % calculateTick($tracking_ticks['profile-pic']) == 0) { tracker_log('[profile-pic #' . $picCount . '] Checking ' . count($tracking_numbers) . ' users.'); foreach ($tracking_numbers as $number) { $wa->sendGetProfilePicture($number, true); } $picCount++; } // 3) DATABASE ACCOUNT REFRESH // // Check user database and refresh user set every hour but with a offset of 80 seconds. if ($pollCount % calculateTick($tracking_ticks['refresh-db']) == calculateTick($tracking_ticks['refresh-db'] - 80)) { retrieveTrackingUsers(true); } // 4) SOCKET RESET AND LOGIN // // Disconnect and reconnect with whatsapp to prevent dead tracker if ($pollCount % calculateTick($tracking_ticks['reset-socket']) == calculateTick($tracking_ticks['reset-socket'] - 40)) { resetSocket(); retrieveTrackingUsers(false); } // 5) DATABASE ACCOUNT VERIFY CHECK // // Verify any freshly inserted accounts and check if there really whatsapp users. // Check everey 5 minutes. // When the user is verified the number is automaticly added to the tracker running DB. if ($pollCount % calculateTick($tracking_ticks['verify-check']) == 0) { verifyTrackingUsers(); } // 6) WHATSAPP PING // // Keep connection alive (<300s) if ($pollCount % calculateTick($tracking_ticks['keep-alive']) == 0) { tracker_log('[keep-alive] Ping sent.', true, false); $wa->sendPing(); } // usage of 39512f5ea29c597f25483697471ac0b00cbb8088359c219e98fa8bdaf7e079fa $pollCount++; // Sleep if no more messages could be processed. if ($poll_took < 1.0) { $sleeping = 1.0 - $poll_took; usleep($sleeping * 1000000); } } }