Exemple #1
0
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>(?:&nbsp;\\((\\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;
}
Exemple #2
0
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'];
}
Exemple #3
0
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;
}
Exemple #4
0
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);
            }
        }
    }
}
Exemple #5
0
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']];
}
Exemple #6
0
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.');
        }
    }
}
Exemple #7
0
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;
    }
}
Exemple #8
0
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;
}
Exemple #9
0
            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!');
Exemple #10
0
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;
}
Exemple #11
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;
}