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 BuildItemDefs() { $itemDB2 = new Reader(__DIR__ . '/../DBC/current/enUS/Item.db2'); $ids = $itemDB2->getIds(); unset($itemDB2); $idMap = []; foreach ($ids as $id) { $idMap[$id] = $id; } DebugMessage("Item ID count: " . count($idMap)); $sparseDB2 = new Reader(__DIR__ . '/../DBC/current/enUS/Item-sparse.db2', [13, 14, 15, 16, 17]); $ids = $sparseDB2->getIds(); unset($sparseDB2); DebugMessage("Sparse ID count: " . count($ids)); foreach ($ids as $id) { unset($idMap[$id]); } DebugMessage("Missing ID count: " . count($idMap)); if (!count($idMap)) { return false; } $idMap = array_reverse($idMap); reset($idMap); $lastId = current($idMap); DebugMessage("Last item ID: {$lastId}"); $lua = "local addonName, addonTable = ...\n"; $lua .= "addonTable.missingItems = {" . implode(',', $idMap) . "}\n"; return $lua; }
function AddHourlyData() { global $db, $caughtKill; if ($caughtKill) { return; } $sqlPattern = <<<'EOF' insert into tblSellerHistoryHourly (seller, `when`, `new%1$s`, `total%1$s`) (select seller, date(`snapshot`), `new`, `total` from tblSellerHistory where hour(`snapshot`) = %2$d order by `snapshot` ) on duplicate key update `new%1$s` = values(`new%1$s`), `total%1$s` = values(`total%1$s`) EOF; for ($hour = 0; $hour < 24; $hour++) { heartbeat(); if ($caughtKill) { break; } $hourPadded = str_pad($hour, 2, '0', STR_PAD_LEFT); $sql = sprintf($sqlPattern, $hourPadded, $hour); $queryOk = $db->real_query($sql); if (!$queryOk) { DebugMessage("SQL error: " . $db->errno . ' ' . $db->error . " - " . substr(preg_replace('/[\\r\\n]/', ' ', $sql), 0, 500), E_USER_WARNING); } else { $rowCount = $db->affected_rows; DebugMessage("{$rowCount} seller hourly rows updated for hour {$hour}"); } } }
function NextMailFile() { $dir = scandir(substr(BOUNCEBACK_PATH, 0, -1), SCANDIR_SORT_ASCENDING); $lockFail = false; $gotFile = false; foreach ($dir as $fileName) { if (preg_match('/^(\\d+)\\./', $fileName, $res)) { if (($handle = fopen(BOUNCEBACK_PATH . $fileName, 'rb')) === false) { continue; } if (!flock($handle, LOCK_EX | LOCK_NB)) { $lockFail = true; fclose($handle); continue; } if (feof($handle)) { fclose($handle); unlink(BOUNCEBACK_PATH . $fileName); continue; } $ts = intval($res[1], 10); $gotFile = $fileName; break; } } unset($dir); if (!$gotFile) { if ($lockFail) { sleep(3); return true; } return false; } DebugMessage('Found message received at ' . date('Y-m-d H:i:s', $ts) . ', ' . TimeDiff($ts)); $message = fread($handle, min(filesize(BOUNCEBACK_PATH . $fileName), 4194304)); ftruncate($handle, 0); fclose($handle); unlink(BOUNCEBACK_PATH . $fileName); $mailId = false; if (preg_match('/X-Undermine-MailID:\\s*([a-zA-Z0-9_-]{27})/', $message, $res)) { $mailId = $res[1]; } elseif (preg_match('/[Mm]essage ID: ([a-zA-Z0-9_-]{27})/', $message, $res)) { $mailId = $res[1]; } if (!$mailId) { DebugMessage('Could not find message ID, forwarding to editor'); NewsstandMail('*****@*****.**', 'The Editor', 'Unparsed notification reply', $message); } else { $address = GetAddressByMailID($mailId); if (!$address) { DebugMessage('Could not find address for mail ID ' . $mailId); } else { $cnt = DisableEmailAddress($address['address']); DebugMessage('Address ' . $address['address'] . ' removed from ' . $cnt . ' account' . ($cnt == 1 ? '' : 's') . '.'); } } return true; }
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 GetRussianOwnerRealms($region) { global $db, $caughtKill; $ruID = 0; $ruSlug = ''; $ruToRun = array(); $stmt = $db->prepare('select id, slug from tblRealm where region=? and locale=\'ru_RU\' and ownerrealm is null'); $stmt->bind_param('s', $region); $stmt->execute(); $stmt->bind_result($ruID, $ruSlug); while ($stmt->fetch()) { heartbeat(); if ($caughtKill) { return; } DebugMessage("Getting ownerrealm for russian slug {$ruSlug}"); $url = GetBattleNetURL($region, 'wow/realm/status?realms=' . $ruSlug . '&locale=ru_RU'); $ruRealm = json_decode(FetchHTTP($url), true, 512, JSON_BIGINT_AS_STRING); if (json_last_error() != JSON_ERROR_NONE) { DebugMessage("{$url} did not return valid JSON"); continue; } if (!isset($ruRealm['realms']) || count($ruRealm['realms']) == 0) { DebugMessage("{$url} returned no realms"); continue; } $ruOwner = str_replace(' ', '', $ruRealm['realms'][0]['name']); $ruToRun[] = sprintf('update tblRealm set ownerrealm = \'%s\' where id = %d', $db->escape_string($ruOwner), $ruID); } $stmt->close(); if ($caughtKill) { return; } foreach ($ruToRun as $sql) { heartbeat(); if ($caughtKill) { return; } if (!$db->real_query($sql)) { DebugMessage(sprintf("%s: %s", $sql, $db->error), E_USER_WARNING); } } }
BnetGet(); exit; } header('Content-type: text/html; charset=UTF-8'); echo <<<EOF <html><head><title>Realm Status</title> <style type="text/css"> td { border-top: 1px solid black } td, th { padding: 4px } td.r { text-align: right } </style></head><body> EOF; echo '<h1>' . date('Y-m-d H:i:s') . '</h1>'; if (!DBConnect()) { DebugMessage('Cannot connect to db!', E_USER_ERROR); } ShowRealms(); ShowMemcacheStats(); ShowLogs(); ShowErrors(); echo '</body></html>'; function BnetGet() { $parts = explode('-', $_GET['bnetget'], 2); if (count($parts) != 2) { echo 'Not enough parts.'; exit; } switch ($parts[0]) { case 'US':
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.'); } } }
<?php require_once __DIR__ . '/../incl/incl.php'; require_once __DIR__ . '/../incl/api.incl.php'; if (!isset($argv[1])) { DebugMessage("Enter IP to ban on command line.\n"); exit(1); } $ip = trim($argv[1]); if ($ip == false) { MCDelete(BANLIST_CACHEKEY); DebugMessage("Cleared banlist from memcache.\n"); exit; } $ret = BanIP($ip); if ($ret) { DebugMessage("{$ip} added to ban list.\n"); } else { if (IPIsBanned($ip)) { DebugMessage("{$ip} already on ban list.\n"); } else { DebugMessage("{$ip} NOT added to ban list.\n"); } }
$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 MakeID() { static $lastTime = 0, $lastIncrement = 0; if ($lastTime != time()) { $lastTime = time(); $lastIncrement = 0; } return ($lastTime << 8) + $lastIncrement++ & 0xffffffff; }
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; }
<?php require_once __DIR__ . '/../incl/incl.php'; require_once __DIR__ . '/../incl/api.incl.php'; if (!isset($argv[1])) { DebugMessage("Enter IP to unban on command line.\n"); exit(1); } $ip = trim($argv[1]); if (!IPIsBanned($ip)) { DebugMessage("{$ip} was not banned.\n"); exit(1); } if (file_exists(BANLIST_FILENAME)) { $lines = shell_exec('grep ' . escapeshellarg("^{$ip} ") . ' ' . escapeshellarg(BANLIST_FILENAME)); if (!$lines) { DebugMessage('Found no lines in ' . BANLIST_FILENAME . "for {$ip}\n"); } else { $other = shell_exec('grep -v ' . escapeshellarg("^{$ip} ") . ' ' . escapeshellarg(BANLIST_FILENAME)); file_put_contents(BANLIST_FILENAME, $other, LOCK_EX); } } else { DebugMessage("Could not find " . BANLIST_FILENAME . "\n"); exit(1); } MCDelete(BANLIST_CACHEKEY); MCDelete(BANLIST_CACHEKEY . '_' . $ip); DebugMessage("{$ip} is unbanned.\n");
function MCHouseLock($house, $waitSeconds = 30) { global $MCHousesLocked; static $registeredShutdown = false; if (isset($MCHousesLocked[$house])) { return true; } $giveUpAt = microtime(true) + $waitSeconds; $me = ['pid' => getmypid(), 'script' => $_SERVER["SCRIPT_FILENAME"], 'when' => time()]; do { if (MCAdd('mchouselock_' . $house, $me, 30 * 60)) { $MCHousesLocked[$house] = true; if (!$registeredShutdown) { $registeredShutdown = true; register_shutdown_function('MCHouseUnlock'); } return true; } usleep(500000); } while ($giveUpAt > microtime(true)); $currentLock = MCGet('mchouselock_' . $house); DebugMessage("Could not get house lock for {$house}, owned by " . $currentLock['pid'] . ' ' . $currentLock['script'] . ' ' . TimeDiff($currentLock['when'])); return false; }
function UpdateGlobalDataJson() { global $caughtKill, $db; if ($caughtKill) { return; } heartbeat(); DebugMessage("Updating global data json"); $stmt = $db->prepare('SELECT item, floor(avg(median)) median FROM tblItemGlobal where bonusset=0 group by item'); $stmt->execute(); $result = $stmt->get_result(); $prices = DBMapArray($result, null); $stmt->close(); $json = []; foreach ($prices as $priceRow) { $json[$priceRow['item']] = $priceRow['median']; } file_put_contents(__DIR__ . '/../public/globalprices.json', json_encode($json, JSON_NUMERIC_CHECK | JSON_FORCE_OBJECT), LOCK_EX); }
function AddDailyData() { global $db, $caughtKill; if ($caughtKill) { return; } $sql = <<<'EOF' select distinct hc.house, date(sn.updated) dt from tblHouseCheck hc join tblSnapshot sn on sn.house = hc.house where ifnull(hc.lastdaily, '2000-01-01') < date(timestampadd(day, -1, now())) and sn.updated > timestampadd(day, 1, ifnull(hc.lastdaily, '2000-01-01')) and sn.updated < date(now()) and sn.flags & 1 = 0 order by 1, 2 EOF; $stmt = $db->prepare($sql); $stmt->execute(); $result = $stmt->get_result(); $houses = $result->fetch_all(MYSQLI_ASSOC); $stmt->close(); DebugMessage(count($houses) . " houses need updates"); $sqlPattern = <<<'EOF' replace into tblItemHistoryDaily (SELECT `ihh`.item, `ihh`.house, `ihh`.`when`, nullif(least( ifnull(silver00, 4294967295),ifnull(silver01, 4294967295),ifnull(silver02, 4294967295),ifnull(silver03, 4294967295), ifnull(silver04, 4294967295),ifnull(silver05, 4294967295),ifnull(silver06, 4294967295),ifnull(silver07, 4294967295), ifnull(silver08, 4294967295),ifnull(silver09, 4294967295),ifnull(silver10, 4294967295),ifnull(silver11, 4294967295), ifnull(silver12, 4294967295),ifnull(silver13, 4294967295),ifnull(silver14, 4294967295),ifnull(silver15, 4294967295), ifnull(silver16, 4294967295),ifnull(silver17, 4294967295),ifnull(silver18, 4294967295),ifnull(silver19, 4294967295), ifnull(silver20, 4294967295),ifnull(silver21, 4294967295),ifnull(silver22, 4294967295),ifnull(silver23, 4294967295)),4294967295) pricemin, round(( ifnull(silver00, 0)+ifnull(silver01, 0)+ifnull(silver02, 0)+ifnull(silver03, 0)+ ifnull(silver04, 0)+ifnull(silver05, 0)+ifnull(silver06, 0)+ifnull(silver07, 0)+ ifnull(silver08, 0)+ifnull(silver09, 0)+ifnull(silver10, 0)+ifnull(silver11, 0)+ ifnull(silver12, 0)+ifnull(silver13, 0)+ifnull(silver14, 0)+ifnull(silver15, 0)+ ifnull(silver16, 0)+ifnull(silver17, 0)+ifnull(silver18, 0)+ifnull(silver19, 0)+ ifnull(silver20, 0)+ifnull(silver21, 0)+ifnull(silver22, 0)+ifnull(silver23, 0) ) / (24 - isnull(silver00)-isnull(silver01)-isnull(silver02)-isnull(silver03)- isnull(silver04)-isnull(silver05)-isnull(silver06)-isnull(silver07)- isnull(silver08)-isnull(silver09)-isnull(silver10)-isnull(silver11)- isnull(silver12)-isnull(silver13)-isnull(silver14)-isnull(silver15)- isnull(silver16)-isnull(silver17)-isnull(silver18)-isnull(silver19)- isnull(silver20)-isnull(silver21)-isnull(silver22)-isnull(silver23))) priceavg, nullif(greatest( ifnull(silver00, 0),ifnull(silver01, 0),ifnull(silver02, 0),ifnull(silver03, 0), ifnull(silver04, 0),ifnull(silver05, 0),ifnull(silver06, 0),ifnull(silver07, 0), ifnull(silver08, 0),ifnull(silver09, 0),ifnull(silver10, 0),ifnull(silver11, 0), ifnull(silver12, 0),ifnull(silver13, 0),ifnull(silver14, 0),ifnull(silver15, 0), ifnull(silver16, 0),ifnull(silver17, 0),ifnull(silver18, 0),ifnull(silver19, 0), ifnull(silver20, 0),ifnull(silver21, 0),ifnull(silver22, 0),ifnull(silver23, 0)),0) pricemax, coalesce( silver00, silver01, silver02, silver03, silver04, silver05, silver06, silver07, silver08, silver09, silver10, silver11, silver12, silver13, silver14, silver15, silver16, silver17, silver18, silver19, silver20, silver21, silver22, silver23) pricestart, coalesce( silver23, silver22, silver21, silver20, silver19, silver18, silver17, silver16, silver15, silver14, silver13, silver12, silver11, silver10, silver09, silver08, silver07, silver06, silver05, silver04, silver03, silver02, silver01, silver00) priceend, nullif(least( ifnull(quantity00, 4294967295),ifnull(quantity01, 4294967295),ifnull(quantity02, 4294967295),ifnull(quantity03, 4294967295), ifnull(quantity04, 4294967295),ifnull(quantity05, 4294967295),ifnull(quantity06, 4294967295),ifnull(quantity07, 4294967295), ifnull(quantity08, 4294967295),ifnull(quantity09, 4294967295),ifnull(quantity10, 4294967295),ifnull(quantity11, 4294967295), ifnull(quantity12, 4294967295),ifnull(quantity13, 4294967295),ifnull(quantity14, 4294967295),ifnull(quantity15, 4294967295), ifnull(quantity16, 4294967295),ifnull(quantity17, 4294967295),ifnull(quantity18, 4294967295),ifnull(quantity19, 4294967295), ifnull(quantity20, 4294967295),ifnull(quantity21, 4294967295),ifnull(quantity22, 4294967295),ifnull(quantity23, 4294967295)),4294967295) quantitymin, round(( ifnull(quantity00, 0)+ifnull(quantity01, 0)+ifnull(quantity02, 0)+ifnull(quantity03, 0)+ ifnull(quantity04, 0)+ifnull(quantity05, 0)+ifnull(quantity06, 0)+ifnull(quantity07, 0)+ ifnull(quantity08, 0)+ifnull(quantity09, 0)+ifnull(quantity10, 0)+ifnull(quantity11, 0)+ ifnull(quantity12, 0)+ifnull(quantity13, 0)+ifnull(quantity14, 0)+ifnull(quantity15, 0)+ ifnull(quantity16, 0)+ifnull(quantity17, 0)+ifnull(quantity18, 0)+ifnull(quantity19, 0)+ ifnull(quantity20, 0)+ifnull(quantity21, 0)+ifnull(quantity22, 0)+ifnull(quantity23, 0) ) / (24 - isnull(quantity00)-isnull(quantity01)-isnull(quantity02)-isnull(quantity03)- isnull(quantity04)-isnull(quantity05)-isnull(quantity06)-isnull(quantity07)- isnull(quantity08)-isnull(quantity09)-isnull(quantity10)-isnull(quantity11)- isnull(quantity12)-isnull(quantity13)-isnull(quantity14)-isnull(quantity15)- isnull(quantity16)-isnull(quantity17)-isnull(quantity18)-isnull(quantity19)- isnull(quantity20)-isnull(quantity21)-isnull(quantity22)-isnull(quantity23))) quantityavg, nullif(greatest( ifnull(quantity00, 0),ifnull(quantity01, 0),ifnull(quantity02, 0),ifnull(quantity03, 0), ifnull(quantity04, 0),ifnull(quantity05, 0),ifnull(quantity06, 0),ifnull(quantity07, 0), ifnull(quantity08, 0),ifnull(quantity09, 0),ifnull(quantity10, 0),ifnull(quantity11, 0), ifnull(quantity12, 0),ifnull(quantity13, 0),ifnull(quantity14, 0),ifnull(quantity15, 0), ifnull(quantity16, 0),ifnull(quantity17, 0),ifnull(quantity18, 0),ifnull(quantity19, 0), ifnull(quantity20, 0),ifnull(quantity21, 0),ifnull(quantity22, 0),ifnull(quantity23, 0)),0) quantitymax FROM `tblItemHistoryHourly` ihh JOIN `tblDBCItem` `i` ON `i`.`id` = `ihh`.`item` WHERE i.stacksize > 1 and ihh.house = ? and ihh.`when` = ?) EOF; foreach ($houses as $houseRow) { heartbeat(); if ($caughtKill) { return; } if (!MCHouseLock($houseRow['house'])) { continue; } $stmt = $db->prepare($sqlPattern); $stmt->bind_param('is', $houseRow['house'], $houseRow['dt']); $queryOk = $stmt->execute(); $rowCount = $db->affected_rows; $stmt->close(); if (!$queryOk) { DebugMessage("SQL error: " . $db->errno . ' ' . $db->error . " - " . substr(preg_replace('/[\\r\\n]/', ' ', $sqlPattern), 0, 500), E_USER_WARNING); $rowCount = -1; } else { DebugMessage("{$rowCount} item daily rows updated for house {$houseRow['house']} for date {$houseRow['dt']}"); } if ($rowCount >= 0) { $stmt = $db->prepare('INSERT INTO tblHouseCheck (house, lastdaily) VALUES (?, ?) ON DUPLICATE KEY UPDATE lastdaily = values(lastdaily)'); $stmt->bind_param('is', $houseRow['house'], $houseRow['dt']); $stmt->execute(); $stmt->close(); } MCHouseUnlock($houseRow['house']); } }
function GetGuild(&$characterNames, $guild, $realmName) { global $db, $caughtKill, $allRealms; heartbeat(); if ($caughtKill) { return; } if (!isset($allRealms[$realmName])) { DebugMessage('Could not find realm ' . $realmName); return; } $guildId = 0; $scanned = 0; $stmt = $db->prepare('select id, ifnull(scanned,\'2000-01-01\') from tblGuild where realm = ? and name = ?'); $stmt->bind_param('is', $allRealms[$realmName]['id'], $guild); $stmt->execute(); $stmt->bind_result($guildId, $scanned); $hasRow = $stmt->fetch() === true; $stmt->close(); if ($hasRow) { if (strtotime($scanned) >= time() - 14 * 24 * 60 * 60) { return; } } else { $stmt = $db->prepare('insert into tblGuild (realm, name) values (?, ?)'); $stmt->bind_param('is', $allRealms[$realmName]['id'], $guild); $stmt->execute(); $stmt->close(); $guildId = $db->insert_id; } DebugMessage("Getting guild <{$guild}> on {$realmName}"); $url = GetBattleNetURL($allRealms[$realmName]['region'], "wow/guild/" . $allRealms[$realmName]['slug'] . "/" . rawurlencode($guild) . "?fields=members"); $json = FetchHTTP($url); if (!$json) { return; } heartbeat(); if ($caughtKill) { return; } $dta = json_decode($json, true); if (json_last_error() != JSON_ERROR_NONE) { return; } if (!isset($dta['members'])) { return; } $charCount = 0; $side = $dta['side'] + 1; foreach ($dta['members'] as $member) { if (!isset($member['character'])) { continue; } if (!isset($member['character']['name'])) { continue; } if (!isset($member['character']['realm'])) { continue; } if (!isset($allRealms[$member['character']['realm']])) { continue; } $charCount++; $member['character']['gender']++; // line up with db enum $stmt = $db->prepare('insert into tblCharacter (name, realm, scanned, race, class, gender, level) values (?, ?, NOW(), ?, ?, ?, ?) on duplicate key update lastmodified=null, scanned=values(scanned), race=values(race), class=values(class), gender=values(gender), level=values(level)'); $stmt->bind_param('siiiii', $member['character']['name'], $allRealms[$member['character']['realm']]['id'], $member['character']['race'], $member['character']['class'], $member['character']['gender'], $member['character']['level']); $stmt->execute(); $stmt->close(); unset($characterNames[$allRealms[$member['character']['realm']]['ownerrealm']][$member['character']['name']]); } $stmt = $db->prepare('update tblGuild set scanned=now(), side=?, members=? where id = ?'); $stmt->bind_param('iii', $side, $charCount, $guildId); $stmt->execute(); $stmt->close(); }
function CategoryDealsItemList($house, $dealsSql, $flags = 0) { global $canCache; $cacheKey = 'category_di4_' . md5($dealsSql) . '_' . $flags; if ($canCache && ($iidList = MCGetHouse($house, $cacheKey)) !== false) { return CategoryDealsItemListCached($house, $iidList, $flags); } $db = DBConnect(); $region = GetRegion($house); $fullSql = <<<EOF select aa.item, aa.bonusset, (select a.id from tblAuction a left join tblAuctionExtra ae on ae.house=a.house and ae.id = a.id join tblDBCItem i on a.item = i.id where a.buy > 0 and a.house=? and a.item=aa.item and ifnull(ae.bonusset,0) = aa.bonusset order by (a.buy/pow(1.15,(cast(ifnull(ae.level, i.level) as signed) - cast(i.level as signed))/15))/a.quantity limit 1) cheapestid from ( select ac.item, ac.bonusset, ac.c_total, ac.c_over, ac.price, gs.median from ( select ab.item, ab.bonusset, count(*) c_total, sum(if(tis2.price > ab.price,1,0)) c_over, ab.price from ( select tis.item, tis.bonusset, tis.price from tblItemSummary tis join tblDBCItem i on tis.item=i.id where tis.house = ? and tis.quantity > 0 and 0 = (select count(*) from tblDBCItemVendorCost ivc where ivc.item=i.id) and i.class not in (16) and {$dealsSql} EOF; if (($flags & CATEGORY_FLAGS_ALLOW_CRAFTED) == 0) { $fullSql .= ' and not exists (select 1 from tblDBCSpell s where s.crafteditem=i.id) '; } if ($flags & CATEGORY_FLAGS_DENY_NONCRAFTED) { $fullSql .= ' and exists (select 1 from tblDBCSpell s where s.crafteditem=i.id) '; } $fullSql .= <<<EOF ) ab join tblItemSummary tis2 on tis2.item = ab.item and tis2.bonusset = ab.bonusset join tblRealm r on r.house = tis2.house and r.canonical is not null where r.region = ? group by ab.item, ab.bonusset ) ac join tblItemGlobal gs on gs.item = ac.item and gs.bonusset = ac.bonusset and gs.region = ? where ((c_over/c_total) > 2/3 or c_total < 15) ) aa where median > 1500000 and median > price order by (cast(median as signed) - cast(price as signed))/greatest(5000000,price) * (c_over/c_total) desc limit 15 EOF; $stmt = $db->stmt_init(); if (!$stmt->prepare($fullSql)) { DebugMessage("Bad SQL: \n" . $fullSql, E_USER_ERROR); } $stmt->bind_param('iiss', $house, $house, $region, $region); $stmt->execute(); $result = $stmt->get_result(); if ($result === false && ($errMsg = $db->error)) { DebugMessage("No result: {$errMsg}\n" . $fullSql, E_USER_ERROR); } $iidList = DBMapArray($result, null); $stmt->close(); MCSetHouse($house, $cacheKey, $iidList); return CategoryDealsItemListCached($house, $iidList, $flags); }
function TransmogGenericItemList($house, $params) { global $db, $canCache; $key = 'transmog_gi2_' . md5(json_encode($params)); if ($canCache && ($tr = MCGetHouse($house, $key)) !== false) { return $tr; } DBConnect(); if (is_array($params)) { $joins = isset($params['joins']) ? $params['joins'] : ''; $where = isset($params['where']) ? ' and ' . $params['where'] : ''; $group = isset($params['group']) ? array_merge($params['group'], [false]) : null; } else { $joins = ''; $group = null; $where = $params == '' ? '' : ' and ' . $params; } $sql = <<<EOF select ab.id, ab.display, ab.buy, ab.class, ab.subclass, ifnull(ab.type, -1 & ab.subclass) `type`, ab.subclassname from ( select aa.*, if(@previd = aa.display, 0, @previd := aa.display) previd from (select @previd := 0) aasetup, ( SELECT i.id, i.display, a.buy, i.class, i.subclass, i.type, concat_ws('-', i.class, i.subclass) subclassname FROM `tblDBCItem` i join tblAuction a on a.item=i.id {$joins} WHERE i.auctionable=1 and i.quality > 1 {$where} and i.display is not null and i.flags & 2 = 0 and a.house = ? and a.buy > 0 order by i.display, a.buy) aa ) ab where ab.previd > 0 EOF; $stmt = $db->prepare($sql); if (!$stmt) { DebugMessage("Bad SQL: \n" . $sql, E_USER_ERROR); } $stmt->bind_param('i', $house); $stmt->execute(); $result = $stmt->get_result(); $tr = DBMapArray($result, $group); $stmt->close(); MCSetHouse($house, $key, $tr); return $tr; }
<?php require_once __DIR__ . '/../incl/incl.php'; require_once __DIR__ . '/../incl/memcache.incl.php'; if (!isset($argv[1])) { DebugMessage('Manual API Maintenance called without time argument. Add expected completion timestamp to command line (or 0 to end maintenance)', E_USER_ERROR); } APIMaintenance($argv[1], isset($argv[2]) ? $argv[2] : false);
<?php chdir(__DIR__); require_once '../incl/incl.php'; require_once '../incl/api.incl.php'; $logFileName = isset($argv[1]) ? $argv[1] : '../logs/access.log'; if (!file_exists($logFileName)) { DebugMessage("Can't find log file: {$logFileName}\n"); exit(1); } $ipHits = []; $fh = fopen($logFileName, 'r'); if ($fh) { while (($line = fgets($fh, 4096)) !== false) { if (strpos($line, '"GET /api/') !== false) { //} && (strpos($line, '(X11; Ubuntu; Linux i686; rv:25.0') !== false)) { preg_match('/^\\S+/', $line, $res); if (!isset($ipHits[$res[0]])) { $ipHits[$res[0]] = 0; } $ipHits[$res[0]]++; } } } fclose($fh); arsort($ipHits, SORT_NUMERIC); $x = 0; foreach ($ipHits as $ip => $hits) { if (!IPIsBanned($ip)) { echo "{$ip} - {$hits}\n"; if (++$x > 30) {
function CleanOldData() { global $db, $caughtKill; if ($caughtKill) { return; } DebugMessage("Starting, getting houses"); $house = null; $houses = []; $stmt = $db->prepare('SELECT DISTINCT house FROM tblRealm WHERE house is not null'); $stmt->execute(); $stmt->bind_result($house); while ($stmt->fetch()) { $houses[] = $house; } $stmt->close(); // clean tblItemHistoryHourly, tblPetHistoryHourly, tblItemHistoryDaily $sqlPatternHourly = 'delete from tbl%sHistoryHourly where house = %d and `when` < \'%s\''; $sqlPatternDaily = 'delete from tblItemHistoryDaily where house = %d and `when` < \'%s\''; for ($hx = 0; $hx < count($houses); $hx++) { heartbeat(); if ($caughtKill) { return; } $house = $houses[$hx]; if (!MCHouseLock($house)) { continue; } $ssDate = ''; $cutoffDateHourly = date('Y-m-d', strtotime('' . HISTORY_DAYS . ' days ago')); $cutOffDateDaily = date('Y-m-d', strtotime('' . HISTORY_DAYS_DEEP . ' days ago')); $stmt = $db->prepare('SELECT date(min(`updated`)) FROM (SELECT `updated` FROM tblSnapshot WHERE house = ? AND `flags` & 1 = 0 ORDER BY updated DESC LIMIT ?) aa'); $maxSnapshots = 24 * HISTORY_DAYS; $stmt->bind_param('ii', $house, $maxSnapshots); $stmt->execute(); $stmt->bind_result($ssDate); $gotDate = $stmt->fetch() === true; $stmt->close(); if (!$gotDate || is_null($ssDate)) { DebugMessage("{$house} has no snapshots, skipping item history!"); MCHouseUnlock($house); continue; } if (strtotime($ssDate) < strtotime($cutoffDateHourly)) { $cutoffDateHourly = $ssDate; } if (!$caughtKill) { $rowCount = DeleteLimitLoop($db, sprintf($sqlPatternHourly, 'Item', $house, $cutoffDateHourly)); DebugMessage("{$rowCount} item hourly history rows deleted from house {$house} since {$cutoffDateHourly}"); } if (!$caughtKill) { $rowCount = DeleteLimitLoop($db, sprintf($sqlPatternHourly, 'Pet', $house, $cutoffDateHourly)); DebugMessage("{$rowCount} pet hourly history rows deleted from house {$house} since {$cutoffDateHourly}"); } if (!$caughtKill) { $rowCount = DeleteLimitLoop($db, sprintf($sqlPatternDaily, $house, $cutOffDateDaily)); DebugMessage("{$rowCount} item history daily rows deleted from house {$house} since {$cutOffDateDaily}"); } MCHouseUnlock($house); } if ($caughtKill) { return; } // clean tblItemExpired for ($hx = 0; $hx < count($houses); $hx++) { heartbeat(); if ($caughtKill) { return; } $house = $houses[$hx]; if (!MCHouseLock($house)) { continue; } $cutoffDate = date('Y-m-d H:i:s', strtotime('' . (HISTORY_DAYS * 2 + 3) . ' days ago')); $sql = sprintf('delete from tblItemExpired where house = %d and `when` < \'%s\'', $house, $cutoffDate); $rowCount = DeleteLimitLoop($db, $sql); DebugMessage(sprintf('%d expired item rows removed from house %d since %s', $rowCount, $house, $cutoffDate)); MCHouseUnlock($house); } if ($caughtKill) { return; } $rowCount = 0; DebugMessage('Clearing out old seller history'); $cutoffDateHourly = date('Y-m-d', strtotime('' . HISTORY_DAYS . ' days ago')); $sql = sprintf('delete from tblSellerHistoryHourly where `when` < \'%s\'', $cutoffDateHourly); $rowCount += DeleteLimitLoop($db, $sql); DebugMessage("{$rowCount} seller history rows deleted in total"); $rowCount = 0; DebugMessage('Clearing out old seller item history'); $sql = 'delete from tblSellerItemHistory where snapshot < timestampadd(day, -' . HISTORY_DAYS . ', now())'; $rowCount += DeleteLimitLoop($db, $sql); DebugMessage("{$rowCount} seller item history rows deleted in total"); $rowCount = 0; $old = date('Y-m-d H:i:s', time() - SUBSCRIPTION_SESSION_LENGTH - 172800); // 2 days older than oldest cookie age DebugMessage('Clearing out user sessions older than ' . $old); $sql = "delete from tblUserSession where lastseen < '{$old}'"; $rowCount += DeleteLimitLoop($db, $sql, 500); DebugMessage("{$rowCount} user session rows deleted in total"); for ($hx = 0; $hx < count($houses); $hx++) { heartbeat(); if ($caughtKill) { return; } $house = $houses[$hx]; if (!MCHouseLock($house)) { continue; } $cutoffDate = date('Y-m-d H:i:s', strtotime('' . (HISTORY_DAYS + 3) . ' days ago')); $sql = sprintf('DELETE FROM tblSnapshot WHERE house = %d AND updated < \'%s\'', $house, $cutoffDate); $db->query($sql); DebugMessage(sprintf('%d snapshot rows removed from house %d since %s', $db->affected_rows, $house, $cutoffDate)); MCHouseUnlock($house); } }
function GetLang($lang) { static $locales = []; if (isset($locales[$lang])) { return $locales[$lang]; } $path = __DIR__ . '/../public/js/locale/' . strtolower($lang) . '.json'; if (!file_exists($path)) { $locales[$lang] = false; return $locales[$lang]; } $json = json_decode(file_get_contents($path), true); if (json_last_error() != JSON_ERROR_NONE) { DebugMessage("Error decoding JSON at {$path}: " . json_last_error_msg()); } if ($lang != 'enus') { $enus = GetLang('enus'); $json = array_replace_recursive($enus, $json); } $locales[$lang] = $json; return $locales[$lang]; }
function SendTweet($msg) { if ($msg == '') { return false; } DebugMessage('Sending tweet of ' . strlen($msg) . " chars:\n" . $msg); global $twitterCredentials; if ($twitterCredentials === false) { return true; } $params = array(); $params['status'] = $msg; $oauth = new OAuth($twitterCredentials['consumerKey'], $twitterCredentials['consumerSecret']); $oauth->setToken($twitterCredentials['WoWTokens']['accessToken'], $twitterCredentials['WoWTokens']['accessTokenSecret']); $url = 'https://api.twitter.com/1.1/statuses/update.json'; try { $didWork = $oauth->fetch($url, $params, 'POST', array('Connection' => 'close')); } catch (OAuthException $e) { $didWork = false; } $ri = $oauth->getLastResponseInfo(); $r = $oauth->getLastResponse(); if ($didWork && $ri['http_code'] == '200') { $json = json_decode($r, true); if (json_last_error() == JSON_ERROR_NONE) { if (isset($json['id_str'])) { return $json['id_str']; } } return true; } if (isset($ri['http_code'])) { DebugMessage('Twitter returned HTTP code ' . $ri['http_code'], E_USER_WARNING); } else { DebugMessage('Twitter returned unknown HTTP code', E_USER_WARNING); } DebugMessage('Twitter returned: ' . print_r($ri, true), E_USER_WARNING); DebugMessage('Twitter returned: ' . print_r($r, true), E_USER_WARNING); return false; }
function DBQueryWithError(&$db, $sql) { $queryOk = $db->query($sql); if (!$queryOk) { DebugMessage("SQL error: " . $db->errno . ' ' . $db->error . " - " . substr(preg_replace('/[\\r\\n]/', ' ', $sql), 0, 500), E_USER_WARNING); } return $queryOk; }
<?php $memcache = new Memcache(); if (!$memcache->connect('127.0.0.1', 11211)) { DebugMessage('Cannot connect to memcached!', E_USER_ERROR); } $memcache->setCompressThreshold(50 * 1024); function MCGet($key) { global $memcache; return $memcache->get('rp_' . $key); } function MCSet($key, $val, $expire = 10800) { global $memcache; return $memcache->set('rp_' . $key, $val, false, $expire); } function MCAdd($key, $val, $expire = 10800) { global $memcache; return $memcache->add('rp_' . $key, $val, false, $expire); } function MCDelete($key) { global $memcache; return $memcache->delete('rp_' . $key); }
function RunMeNTimes($howMany = 1) { global $argv; if (php_sapi_name() != 'cli') { DebugMessage('Cannot run once if not CLI', E_USER_WARNING); return; } if (intval(shell_exec('ps -o args -C php | grep ' . escapeshellarg(implode(' ', $argv)) . ' | wc -l')) > $howMany) { die; } }
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 MakeZip($zipPath = false) { DebugMessage('Making zip file..'); $zipFilename = tempnam('/tmp', 'addonzip'); $zip = new ZipArchive(); if (!$zip->open($zipFilename, ZipArchive::CREATE)) { @unlink($zipFilename); DebugMessage('Could not create zip file', E_USER_ERROR); } $tocFile = file_get_contents('../addon/TheUndermineJournal.toc'); $tocFile = sprintf($tocFile, date('D, F j'), date('Ymd')); $zip->addFromString("TheUndermineJournal/TheUndermineJournal.toc", $tocFile); RecursiveAddToZip($zip, '../addon/libs/', 'TheUndermineJournal/libs/'); $zip->addFile('../addon/GetDetailedItemLevelInfo.lua', "TheUndermineJournal/GetDetailedItemLevelInfo.lua"); $zip->addFile('../addon/BonusSets.lua', "TheUndermineJournal/BonusSets.lua"); $zip->addFile('../addon/TheUndermineJournal.lua', "TheUndermineJournal/TheUndermineJournal.lua"); $zip->addFile('../addon/MarketData-US.lua', "TheUndermineJournal/MarketData-US.lua"); $zip->addFile('../addon/MarketData-EU.lua', "TheUndermineJournal/MarketData-EU.lua"); //$zip->addFromString("TheUndermineJournal/SpellToItem.lua",getspelltoitemlua()); $zip->close(); if (!$zipPath) { $zipPath = '../addon/TheUndermineJournal.zip'; } rename($zipFilename, $zipPath); }
$stmt->bind_param('s', $region); $stmt->execute(); $result = $stmt->get_result(); $slugs = DBMapArray($result, null); $stmt->close(); foreach ($slugs as $slug) { if ($caughtKill) { break; } file_put_contents($publicDir . '/' . strtolower($region) . '-' . $slug . '.html', cookhtml($region, $slug)); $siteMap .= '<url><loc>https://realmpop.com/' . strtolower($region) . '-' . $slug . '.html</loc><priority>0.5</priority><changefreq>weekly</changefreq></url>' . "\n"; } } $siteMap .= "</urlset>"; file_put_contents($publicDir . '/sitemap.xml', $siteMap); DebugMessage('Done! Started ' . TimeDiff($startTime)); // PRETTYREALM SLUG REALMBIO REALMSET JSONSIZE function cookhtml($realmset, $slug = '') { global $publicDir, $db; global $htmlheader, $htmlrealm, $htmlend, $htmlrealmset; $jsonfn = $publicDir . '/' . strtolower($realmset) . ($slug != '' ? '-' : '') . $slug . '.json'; $jsonsize = file_exists($jsonfn) ? filesize($jsonfn) : 0; if ($slug != '') { $html = $htmlheader . $htmlrealm . $htmlend; $realmset = substr(strtoupper($realmset), 0, 2); $realmslug = substr($slug, 0, 45); $sql = <<<EOF select r.*, if(r.region='US',if(locale='pt_BR', 'Brazil', if(locale='es_MX', 'Latin America', if(timezone like 'Australia/%', 'Oceanic', 'United States'))), case locale when 'de_DE' then 'German'