function GetReagents($spell) { $cacheFile = __DIR__ . '/reagents.cache/' . $spell . '.json'; if (file_exists($cacheFile)) { return json_decode(file_get_contents($cacheFile), true); } $power = \Newsstand\HTTP::Get(sprintf('http://www.wowhead.com/spell=%d&power', $spell)); if (!$power) { DebugMessage("Spell {$spell} could not be fetched from Wowhead.", E_USER_NOTICE); return false; } $reagents = []; $c = preg_match('/Reagents:<br[^>]*><div\\b[^>]*>([\\w\\W]+?)<\\/div>/', $power, $res); if ($c > 0) { $itemsHtml = $res[1]; $c = preg_match_all('/<a href="\\/item=(\\d+)">[^<]*<\\/a>(?: \\((\\d+)\\))?/', $itemsHtml, $res); for ($x = 0; $x < $c; $x++) { $itemId = $res[1][$x]; $qty = intval($res[2][$x]); if (!$qty) { $qty = 1; } if (!isset($reagents[$itemId])) { $reagents[$itemId] = 0; } $reagents[$itemId] += $qty; } } else { DebugMessage("Spell {$spell} has no reagents.", E_USER_NOTICE); } file_put_contents($cacheFile, json_encode($reagents, JSON_NUMERIC_CHECK)); return $reagents; }
function GetLatestGameVersionID() { $url = sprintf("https://wow.curseforge.com/api/game/versions?token=%s", CURSEFORGE_API_TOKEN); $json = HTTP::Get($url); if (!$json) { trigger_error("Empty response from curseforge game versions"); return false; } $json = json_decode($json, true); if (json_last_error() != JSON_ERROR_NONE) { trigger_error("Invalid json response from curseforge game versions"); return false; } if (!count($json) || !isset($json[0]['id']) || !isset($json[0]['name'])) { trigger_error("Unknown json response from curseforge game versions"); return false; } usort($json, function ($a, $b) { return version_compare($a['name'], $b['name']); }); $latest = array_pop($json); return $latest['id']; }
function FetchRegionData($region) { global $caughtKill; $region = trim(strtolower($region)); $results = []; DebugMessage("Fetching realms for {$region}"); $url = GetBattleNetURL($region, 'wow/realm/status'); $jsonString = HTTP::Get($url); $json = json_decode($jsonString, true); if (json_last_error() != JSON_ERROR_NONE) { DebugMessage("Error decoding " . strlen($jsonString) . " length JSON string for {$region}: " . json_last_error_msg(), E_USER_WARNING); return $results; } if (!isset($json['realms'])) { DebugMessage("Did not find realms in realm status JSON for {$region}", E_USER_WARNING); return $results; } $slugMap = []; foreach ($json['realms'] as $realmRow) { if ($caughtKill) { break; } if (!isset($realmRow['slug'])) { continue; } $slug = $realmRow['slug']; if (isset($results[$slug])) { $results[$slug]['name'] = $realmRow['name']; continue; } $resultRow = ['name' => $realmRow['name'], 'canonical' => 1]; $results[$slug] = $resultRow; $slugMap[$slug] = [$slug]; if (isset($realmRow['connected_realms'])) { foreach ($realmRow['connected_realms'] as $connectedSlug) { if ($connectedSlug == $slug) { continue; } $results[$connectedSlug] = ['name' => '']; $slugMap[$slug][] = $connectedSlug; } } } $chunks = array_chunk($slugMap, REALM_CHUNK_SIZE, true); foreach ($chunks as $chunk) { DebugMessage("Fetching auction data for {$region} " . implode(', ', array_keys($chunk))); $urls = []; foreach (array_keys($chunk) as $slug) { $urls[$slug] = GetBattleNetURL($region, 'wow/auction/data/' . $slug); } $started = JSNow(); $dataUrls = []; $jsons = FetchURLBatch($urls); foreach ($chunk as $slug => $slugs) { $json = []; if (!isset($jsons[$slug])) { DebugMessage("No HTTP response for {$region} {$slug}", E_USER_WARNING); } else { $json = json_decode($jsons[$slug], true); if (json_last_error() != JSON_ERROR_NONE) { DebugMessage("Error decoding JSON string for {$region} {$slug}: " . json_last_error_msg(), E_USER_WARNING); $json = []; } } $modified = isset($json['files'][0]['lastModified']) ? $json['files'][0]['lastModified'] : 0; $url = isset($json['files'][0]['url']) ? $json['files'][0]['url'] : ''; if ($url) { $dataUrls[$slug] = $url; } foreach ($slugs as $connectedSlug) { $results[$connectedSlug]['checked'] = $started; $results[$connectedSlug]['modified'] = $modified; } } $dataHeads = FetchURLBatch($dataUrls, [CURLOPT_HEADER => true, CURLOPT_NOBODY => true]); foreach ($chunk as $slug => $slugs) { $fileDate = 0; if (isset($dataHeads[$slug])) { if (preg_match('/(?:^|\\n)Last-Modified: ([^\\n]+)/i', $dataHeads[$slug], $res)) { $fileDate = strtotime($res[1]) * 1000; } elseif ($dataHeads[$slug]) { DebugMessage("Found no last-modified header for {$region} {$slug} at " . $dataUrls[$slug] . "\n" . $dataHeads[$slug], E_USER_WARNING); } } elseif (isset($dataUrls[$slug])) { DebugMessage("Fetched no header for {$region} {$slug} at " . $dataUrls[$slug], E_USER_WARNING); } foreach ($slugs as $connectedSlug) { $results[$connectedSlug]['file'] = $fileDate; } } } ksort($results); return $results; }
function GetRealmPopulation($region) { global $db, $caughtKill; $json = \Newsstand\HTTP::Get('https://realmpop.com/' . strtolower($region) . '.json'); if (!$json) { DebugMessage('Could not get realmpop json for ' . $region, E_USER_WARNING); return; } if ($caughtKill) { return; } $stats = json_decode($json, true); if (json_last_error() != JSON_ERROR_NONE) { DebugMessage('json decode error for realmpop json for ' . $region, E_USER_WARNING); return; } $stats = $stats['realms']; $stmt = $db->prepare('SELECT slug, id FROM tblRealm WHERE region=?'); $stmt->bind_param('s', $region); $stmt->execute(); $result = $stmt->get_result(); $bySlug = DBMapArray($result); $stmt->close(); if ($caughtKill) { return; } $sqlPattern = 'UPDATE tblRealm SET population = %d WHERE id = %d'; foreach ($stats as $slug => $o) { if (isset($bySlug[$slug])) { $sql = sprintf($sqlPattern, $o['counts']['Alliance'] + $o['counts']['Horde'], $bySlug[$slug]['id']); if (!$db->real_query($sql)) { DebugMessage(sprintf("%s: %s", $sql, $db->error), E_USER_WARNING); } } } }
function CreateBitPayInvoice($loginState) { $paid = GetIsPaid($loginState); if (!$paid['accept']) { DebugMessage("User " . $loginState['id'] . " attempted to create BitPay invoice when we would not accept payments."); return []; } $priceAmount = preg_match('/\\d+\\.\\d+/', SUBSCRIPTION_PAID_PRICE, $res) ? $res[0] : false; $priceCurrency = preg_match('/\\b[A-Z]{3}\\b/', SUBSCRIPTION_PAID_PRICE, $res) ? $res[0] : false; if (!$priceAmount || !$priceCurrency) { DebugMessage("Could not find price ({$priceAmount}) or currency ({$priceCurrency}) from " . SUBSCRIPTION_PAID_PRICE . " when creating BitPay invoice."); return []; } $LANG = GetLang($loginState['locale']); $toPost = ['price' => $priceAmount, 'currency' => $priceCurrency, 'posData' => $paid['accept']['custom'], 'fullNotifications' => true, 'notificationURL' => SUBSCRIPTION_BITPAY_IPN_URL, 'notificationEmail' => SUBSCRIPTION_ERRORS_EMAIL_ADDRESS, 'redirectURL' => 'https://' . strtolower($_SERVER["HTTP_HOST"]) . '/#subscription', 'itemDesc' => $LANG['paidSubscription'] . ' - ' . round(SUBSCRIPTION_PAID_ADDS_SECONDS / 86400) . ' ' . $LANG['timeDays']]; $headers = ['Content-Type: application/json; charset=utf-8', 'Authorization: Basic ' . base64_encode(SUBSCRIPTION_BITPAY_KEY . ':')]; $jsonString = \Newsstand\HTTP::Post(SUBSCRIPTION_BITPAY_INVOICE_URL, json_encode($toPost), $headers); if (!$jsonString) { DebugMessage("No response from BitPay having sent:\n" . json_encode($toPost) . "\n" . print_r($headers, true)); return []; } $json = json_decode($jsonString, true); if (json_last_error() != JSON_ERROR_NONE) { DebugMessage("Invalid response from BitPay:\n{$jsonString}\nHaving sent:\n" . json_encode($toPost) . "\n" . print_r($headers, true)); return []; } if (!UpdateBitPayTransaction($json, true)) { return []; } return ['url' => $json['url']]; }
function SendAndroidNotifications($regions) { global $db; global $timeZones, $timeLeftCodes, $regionNames; $sent = []; $AndroidEndpoint = 'https://android.googleapis.com/gcm/send'; foreach ($regions as $region) { $properRegion = strtoupper($region); if ($properRegion == 'US') { $properRegion = 'NA'; } $sql = 'select * from tblWowToken w where region = ? order by `when` desc limit 2'; $stmt = $db->prepare($sql); $stmt->bind_param('s', $region); $stmt->execute(); $result = $stmt->get_result(); $bothTokenData = DBMapArray($result, null); $tokenData = array_shift($bothTokenData); $prevTokenData = count($bothTokenData) ? array_shift($bothTokenData) : []; $stmt->close(); if (!$prevTokenData) { continue; } if ($tokenData['marketgold'] == $prevTokenData['marketgold']) { continue; } if ($tokenData['result'] != 1 || $tokenData['result'] != $prevTokenData['result']) { continue; } $d = new DateTime('now', timezone_open($timeZones[$region])); $d->setTimestamp(strtotime($tokenData['when'])); $formatted = ['BUY' => number_format($tokenData['marketgold']), 'TIMETOSELL' => isset($timeLeftCodes[$tokenData['timeleft']]) ? $timeLeftCodes[$tokenData['timeleft']] : $tokenData['timeleft'], 'UPDATED' => $d->format('M j g:ia T')]; $direction = $tokenData['marketgold'] > $prevTokenData['marketgold'] ? 'over' : 'under'; $sql = <<<EOF select s.endpoint, s.id, e.region, e.value from tblWowTokenSubs s join tblWowTokenEvents e on e.subid = s.id where s.lastfail is null and e.direction = '{$direction}' and e.region = '{$properRegion}' and s.endpoint like '{$AndroidEndpoint}%' EOF; if ($direction == 'over') { $sql .= ' and e.value >= ' . $prevTokenData['marketgold'] . ' and e.value < ' . $tokenData['marketgold']; } else { $sql .= ' and e.value <= ' . $prevTokenData['marketgold'] . ' and e.value > ' . $tokenData['marketgold']; } $stmt = $db->prepare($sql); $stmt->execute(); $result = $stmt->get_result(); $rows = DBMapArray($result, ['id']); $stmt->close(); if (count($rows) == 0) { continue; } //sells in " . $formatted['TIMETOSELL'] . ' $message = $regionNames[$properRegion] . ' price %s: now ' . $formatted['BUY'] . "g, as of " . $formatted['UPDATED'] . '.'; $chunks = array_chunk($rows, 50, true); foreach ($chunks as $chunk) { $lookup = []; $toSend = []; $failed = []; $successful = []; foreach ($chunk as $id => $row) { $registrationId = substr($row['endpoint'], strlen($AndroidEndpoint) + 1); $msg = sprintf($message, $direction . ' ' . number_format($row['value'], 0) . 'g'); $key = md5($row['endpoint']); if (!isset($sent[$key])) { $lookup[] = $id; $toSend[] = $registrationId; $sent[$key] = $msg; } else { $sent[$key] .= " \n" . $msg; } MCSet('tokennotify-' . $key, $sent[$key], 8 * 60 * 60); } if (!count($toSend)) { continue; } $toSend = json_encode(['registration_ids' => $toSend, 'time_to_live' => 4 * 60 * 60]); $headers = ['Authorization: key=' . ANDROID_GCM_KEY, 'Content-Type: application/json']; $outHeaders = []; $ret = \Newsstand\HTTP::Post($AndroidEndpoint, $toSend, $headers, $outHeaders); $ret = json_decode($ret, true); if (json_last_error() != JSON_ERROR_NONE || !isset($ret['results'])) { if (count($lookup) == 1 && isset($outHeaders['responseCode']) && $outHeaders['responseCode'] == '404') { // only sent one, which failed, so mark it as failed $successful = []; $failed = $lookup; } else { // can only assume all went through DebugMessage("Bad response from {$AndroidEndpoint}\n" . print_r($headers, true) . $toSend . "\n" . print_r($outHeaders, true) . "\n{$ret}"); $successful = $lookup; $failed = []; } } else { for ($x = 0; $x < count($ret['results']); $x++) { if (isset($ret['results'][$x]['error'])) { $failed[] = $lookup[$x]; } else { $successful[] = $lookup[$x]; } } } $stmt = $db->prepare('update tblWowTokenEvents set lasttrigger=now() where subid in (' . implode(',', $lookup) . ') and region=\'' . $properRegion . '\' and direction=\'' . $direction . '\''); $stmt->execute(); $stmt->close(); if (count($successful)) { $stmt = $db->prepare('update tblWowTokenSubs set lastpush=now() where id in (' . implode(',', $successful) . ')'); $stmt->execute(); $stmt->close(); } if (count($failed)) { $stmt = $db->prepare('update tblWowTokenSubs set lastpush=now(), lastfail=now() where id in (' . implode(',', $failed) . ')'); $stmt->execute(); $stmt->close(); } DebugMessage('Sent ' . count($lookup) . ' messages to ' . $AndroidEndpoint . ' - ' . count($successful) . ' successful, ' . count($failed) . ' failed.'); } } }
function UploadTweetMedia($data) { global $twitterCredentials; if ($twitterCredentials === false) { return false; } $boundary = ''; $mimedata['media'] = "content-disposition: form-data; name=\"media\"\r\nContent-Type: image/png\r\nContent-Transfer-Encoding: binary\r\n\r\n" . $data; while ($boundary == '') { for ($x = 0; $x < 16; $x++) { $boundary .= chr(rand(ord('a'), ord('z'))); } foreach ($mimedata as $d) { if (strpos($d, $boundary) !== false) { $boundary = ''; } } } $mime = ''; foreach ($mimedata as $d) { $mime .= "--{$boundary}\r\n{$d}\r\n"; } $mime .= "--{$boundary}--\r\n"; $oauth = new OAuth($twitterCredentials['consumerKey'], $twitterCredentials['consumerSecret']); $oauth->setToken($twitterCredentials[TWITTER_ACCOUNT]['accessToken'], $twitterCredentials[TWITTER_ACCOUNT]['accessTokenSecret']); $url = 'https://upload.twitter.com/1.1/media/upload.json'; $requestHeader = $oauth->getRequestHeader('POST', $url); $inHeaders = ["Authorization: {$requestHeader}", 'Content-Type: multipart/form-data; boundary=' . $boundary]; $outHeaders = []; $ret = \Newsstand\HTTP::Post($url, $mime, $inHeaders, $outHeaders); if ($ret) { $json = json_decode($ret, true); if (json_last_error() == JSON_ERROR_NONE) { if (isset($json['media_id_string'])) { return $json['media_id_string']; } else { DebugMessage('Parsed JSON response from post to twitter, no media id', E_USER_WARNING); DebugMessage(print_r($json, true), E_USER_WARNING); return false; } } else { DebugMessage('Non-JSON response from post to twitter', E_USER_WARNING); DebugMessage($ret, E_USER_WARNING); return false; } } else { DebugMessage('No/bad response from post to twitter', E_USER_WARNING); DebugMessage(print_r($outHeaders, true), E_USER_WARNING); return false; } }
function ValidatePaypalNotification($rawPost, $useSandbox = false) { $url = sprintf('https://www%s.paypal.com/cgi-bin/webscr', $useSandbox ? '.sandbox' : ''); LogPaypalMessage("Validating {$rawPost}"); $result = \Newsstand\HTTP::Post($url, 'cmd=_notify-validate&' . $rawPost); if (trim($result) == 'VERIFIED') { return true; } return $result; }
exit; } $c = $json['members'][$y]['character']; if ($c['level'] < 20) { continue; } $toon = $c['name']; DebugMessage("Fetching {$region} {$slug} {$toon} of <{$guild}>"); $url = GetBattleNetURL($region, "wow/character/{$slug}/{$toon}?fields=appearance"); $cjson = json_decode(\Newsstand\HTTP::Get($url), true); if (!isset($cjson['appearance'])) { continue; } $imgUrl = "http://render-{$region}.worldofwarcraft.com/character/" . preg_replace('/-avatar\\.jpg$/', '-inset.jpg', $cjson['thumbnail']); DebugMessage("Fetching {$imgUrl}"); $img = \Newsstand\HTTP::Get($imgUrl); if ($img) { $hits++; $id = MakeID(); $helm = $cjson['appearance']['showHelm'] ? 1 : 0; DebugMessage("Saving {$id} as {$c['race']} {$c['gender']} {$helm}"); file_put_contents(CAPTCHA_DIR . '/' . $id . '.jpg', $img); $sql = 'INSERT INTO tblCaptcha (id, race, gender, helm) VALUES (?, ?, ?, ?)'; $stmt = $db->prepare($sql); $stmt->bind_param('iiii', $id, $c['race'], $c['gender'], $helm); $stmt->execute(); $stmt->close(); } } } DebugMessage('Done!');
function FetchSnapshot() { global $db, $region; $lockName = "fetchsnapshot_{$region}"; $stmt = $db->prepare('select get_lock(?, 30)'); $stmt->bind_param('s', $lockName); $stmt->execute(); $lockSuccess = null; $stmt->bind_result($lockSuccess); if (!$stmt->fetch()) { $lockSuccess = null; } $stmt->close(); if ($lockSuccess != '1') { DebugMessage("Could not get mysql lock for {$lockName}."); return 30; } $earlyCheckSeconds = EARLY_CHECK_SECONDS; $nextRealmSql = <<<ENDSQL select r.house, min(r.canonical), count(*) c, ifnull(hc.nextcheck, s.nextcheck) upd, s.lastupdate, s.mindelta, hc.lastchecksuccessresult from tblRealm r left join ( select deltas.house, timestampadd(second, least(ifnull(min(delta)-{$earlyCheckSeconds}, 45*60), 150*60), max(deltas.updated)) nextcheck, max(deltas.updated) lastupdate, min(delta) mindelta from ( select sn.updated, if(@prevhouse = sn.house and sn.updated > timestampadd(hour, -72, now()), unix_timestamp(sn.updated) - @prevdate, null) delta, @prevdate := unix_timestamp(sn.updated) updated_ts, @prevhouse := sn.house house from (select @prevhouse := null, @prevdate := null) setup, tblSnapshot sn order by sn.house, sn.updated) deltas group by deltas.house ) s on s.house = r.house left join tblHouseCheck hc on hc.house = r.house where r.region = ? and r.house is not null and r.canonical is not null group by r.house order by ifnull(upd, '2000-01-01') asc, c desc, r.house asc limit 1 ENDSQL; $house = $slug = $realmCount = $nextDate = $lastDate = $minDelta = $lastSuccessJson = null; $stmt = $db->prepare($nextRealmSql); $stmt->bind_param('s', $region); $stmt->execute(); $stmt->bind_result($house, $slug, $realmCount, $nextDate, $lastDate, $minDelta, $lastSuccessJson); $gotRealm = $stmt->fetch() === true; $stmt->close(); if (!$gotRealm) { DebugMessage("No {$region} realms to fetch!"); ReleaseDBLock($lockName); return 30; } if (strtotime($nextDate) > time() && strtotime($nextDate) < time() + 3.5 * 60 * 60) { $delay = strtotime($nextDate) - time(); DebugMessage("No {$region} realms ready yet, waiting " . SecondsOrMinutes($delay) . "."); ReleaseDBLock($lockName); return $delay; } SetHouseNextCheck($house, time() + 600, null); ReleaseDBLock($lockName); DebugMessage("{$region} {$slug} fetch for house {$house} to update {$realmCount} realms, due since " . (is_null($nextDate) ? 'unknown' : SecondsOrMinutes(time() - strtotime($nextDate)) . ' ago')); $url = GetBattleNetURL($region, "wow/auction/data/{$slug}"); $outHeaders = []; $dta = []; $json = \Newsstand\HTTP::Get($url, [], $outHeaders); if ($json === false && isset($outHeaders['body'])) { // happens if server returns non-200 code, but we'll want that json anyway $json = $outHeaders['body']; } if ($json !== false) { $dta = json_decode($json, true); if (json_last_error() != JSON_ERROR_NONE) { $dta = []; } } if (!isset($dta['files']) && !is_null($lastSuccessJson)) { // no files in current status json, probably "internal server error" // check the headers on our last known good data url $lastGoodDta = json_decode($lastSuccessJson, true); if (json_last_error() != JSON_ERROR_NONE) { DebugMessage("{$region} {$slug} invalid JSON for last successful json\n" . $lastSuccessJson, E_USER_WARNING); } elseif (!isset($lastGoodDta['files'])) { DebugMessage("{$region} {$slug} no files in the last success json?!", E_USER_WARNING); } else { usort($lastGoodDta['files'], 'AuctionFileSort'); $fileInfo = end($lastGoodDta['files']); $oldModified = ceil(intval($fileInfo['lastModified'], 10) / 1000); DebugMessage("{$region} {$slug} returned no files. Checking headers on URL from " . date('Y-m-d H:i:s', $oldModified)); $headers = \Newsstand\HTTP::Head(preg_replace('/^http:/', 'https:', $fileInfo['url'])); if (isset($headers['Last-Modified'])) { $newModified = strtotime($headers['Last-Modified']); $fileInfo['lastModified'] = $newModified * 1000; $dta['files'] = [$fileInfo]; if (abs($oldModified - $newModified) < 10) { DebugMessage("{$region} {$slug} data file has unchanged last modified date from last successful parse."); } else { DebugMessage("{$region} {$slug} data file modified " . date('Y-m-d H:i:s', $newModified) . "."); } } else { DebugMessage("{$region} {$slug} data file failed fetching last modified date via HEAD method."); } } } if (!isset($dta['files'])) { $delay = GetCheckDelay(strtotime($lastDate)); DebugMessage("{$region} {$slug} returned no files. Waiting " . SecondsOrMinutes($delay) . ".", E_USER_WARNING); SetHouseNextCheck($house, time() + $delay, $json); \Newsstand\HTTP::AbandonConnections(); return 0; } usort($dta['files'], 'AuctionFileSort'); $fileInfo = end($dta['files']); $modified = ceil(intval($fileInfo['lastModified'], 10) / 1000); $lastDateUnix = is_null($lastDate) ? $modified - 1 : strtotime($lastDate); $delay = 0; if (!is_null($minDelta) && $modified <= $lastDateUnix) { if ($lastDateUnix + $minDelta > time()) { // we checked for an earlier-than-expected snapshot, didn't see one $delay = $lastDateUnix + $minDelta - time() + 8; // next check will be 8 seconds after expected update } else { if ($lastDateUnix + $minDelta + 45 > time()) { // this is the first check after we expected a new snapshot, but didn't see one. // don't trust api, assume data file URL won't change, and check last-modified time on data file $headers = \Newsstand\HTTP::Head(preg_replace('/^http:/', 'https:', $fileInfo['url'])); if (isset($headers['Last-Modified'])) { $newModified = strtotime($headers['Last-Modified']); if ($newModified > $modified) { DebugMessage("{$region} {$slug} data file indicates last modified {$newModified} " . date('H:i:s', $newModified) . ", ignoring API result."); $modified = $newModified; } else { if ($newModified == $modified) { DebugMessage("{$region} {$slug} data file has last modified date matching API result."); } else { DebugMessage("{$region} {$slug} data file has last modified date earlier than API result: {$newModified} " . date('H:i:s', $newModified) . "."); } } } else { DebugMessage("{$region} {$slug} data file failed fetching last modified date via HEAD method."); } } } } if ($modified <= $lastDateUnix) { if ($delay <= 0) { $delay = GetCheckDelay($modified); } DebugMessage("{$region} {$slug} still not updated since {$modified} " . date('H:i:s', $modified) . " (" . SecondsOrMinutes(time() - $modified) . " ago). Waiting " . SecondsOrMinutes($delay) . "."); SetHouseNextCheck($house, time() + $delay, $json); return 0; } DebugMessage("{$region} {$slug} updated {$modified} " . date('H:i:s', $modified) . " (" . SecondsOrMinutes(time() - $modified) . " ago), fetching auction data file"); $dlStart = microtime(true); $data = \Newsstand\HTTP::Get(preg_replace('/^http:/', 'https:', $fileInfo['url']), [], $outHeaders); $dlDuration = microtime(true) - $dlStart; if (!$data || substr($data, -4) != "]\r\n}") { if (!$data) { DebugMessage("{$region} {$slug} data file empty. Waiting 5 seconds and trying again."); sleep(5); } else { DebugMessage("{$region} {$slug} data file malformed. Waiting 10 seconds and trying again."); sleep(10); } $dlStart = microtime(true); $data = \Newsstand\HTTP::Get($fileInfo['url'] . (parse_url($fileInfo['url'], PHP_URL_QUERY) ? '&' : '?') . 'please', [], $outHeaders); $dlDuration = microtime(true) - $dlStart; } if (!$data) { DebugMessage("{$region} {$slug} data file empty. Will try again in 30 seconds."); SetHouseNextCheck($house, time() + 30, $json); \Newsstand\HTTP::AbandonConnections(); return 10; } if (substr($data, -4) != "]\r\n}") { $delay = GetCheckDelay($modified); DebugMessage("{$region} {$slug} data file still probably malformed. Waiting " . SecondsOrMinutes($delay) . "."); SetHouseNextCheck($house, time() + $delay, $json); return 0; } $xferBytes = isset($outHeaders['X-Original-Content-Length']) ? $outHeaders['X-Original-Content-Length'] : strlen($data); DebugMessage("{$region} {$slug} data file " . strlen($data) . " bytes" . ($xferBytes != strlen($data) ? ' (transfer length ' . $xferBytes . ', ' . round($xferBytes / strlen($data) * 100, 1) . '%)' : '') . ", " . round($dlDuration, 2) . "sec, " . round($xferBytes / 1000 / $dlDuration) . "KBps"); if ($xferBytes >= strlen($data) && strlen($data) > 65536) { DebugMessage('No compression? ' . print_r($outHeaders, true)); } if ($xferBytes / 1000 / $dlDuration < 200 && in_array($region, ['US', 'EU'])) { DebugMessage("Speed under 200KBps, closing persistent connections"); \Newsstand\HTTP::AbandonConnections(); } $successJson = json_encode($dta); // will include any updates from using lastSuccessJson $stmt = $db->prepare('INSERT INTO tblHouseCheck (house, nextcheck, lastcheck, lastcheckresult, lastchecksuccess, lastchecksuccessresult) VALUES (?, NULL, now(), ?, now(), ?) ON DUPLICATE KEY UPDATE nextcheck=values(nextcheck), lastcheck=values(lastcheck), lastcheckresult=values(lastcheckresult), lastchecksuccess=values(lastchecksuccess), lastchecksuccessresult=values(lastchecksuccessresult)'); $stmt->bind_param('iss', $house, $json, $successJson); $stmt->execute(); $stmt->close(); $stmt = $db->prepare('INSERT INTO tblSnapshot (house, updated) VALUES (?, from_unixtime(?))'); $stmt->bind_param('ii', $house, $modified); $stmt->execute(); $stmt->close(); MCSet('housecheck_' . $house, time(), 0); $fileName = "{$modified}-" . str_pad($house, 5, '0', STR_PAD_LEFT) . ".json"; file_put_contents(SNAPSHOT_PATH . $fileName, $data, LOCK_EX); link(SNAPSHOT_PATH . $fileName, SNAPSHOT_PATH . 'parse/' . $fileName); if (in_array($region, ['US', 'EU'])) { link(SNAPSHOT_PATH . $fileName, SNAPSHOT_PATH . 'watch/' . $fileName); } unlink(SNAPSHOT_PATH . $fileName); return 0; }
function FetchPets($pets) { global $petMap; $results = array(); foreach ($pets as &$id) { heartbeat(); DebugMessage('Fetching pet ' . $id); $url = GetBattleNetURL('us', 'wow/battlePet/species/' . $id); $json = \Newsstand\HTTP::Get($url); $dta = json_decode($json, true); if (json_last_error() != JSON_ERROR_NONE || !isset($dta['speciesId'])) { DebugMessage('Error fetching pet ' . $id . ' from battle.net..'); continue; } $results[$dta['speciesId']] = array('json' => $json); foreach ($petMap as $ours => $details) { if (!isset($dta[$details['name']])) { if ($details['required']) { DebugMessage('Pet ' . $dta['speciesId'] . ' did not have required column ' . $details['name'], E_USER_WARNING); unset($results[$dta['speciesId']]); continue 2; } $dta[$details['name']] = null; } if (is_bool($dta[$details['name']])) { $results[$dta['speciesId']][$ours] = $dta[$details['name']] ? 1 : 0; } else { $results[$dta['speciesId']][$ours] = $dta[$details['name']]; } } } return $results; }