Exemple #1
0
function xchange796_query($appid, $apikey, $secretkey, $url)
{
    // from https://796.com/wiki.html
    $timestamp = time();
    $params = array('appid' => $appid, 'apikey' => $apikey, 'secretkey' => $secretkey, 'timestamp' => $timestamp);
    ksort($params);
    // "be careful that the sequence is quite important"
    $param_uri = http_build_query($params, '', '&');
    $sig = base64_encode(hash_hmac('sha1', $param_uri, $secretkey));
    $token_url = url_add("https://796.com/oauth/token", array('appid' => $appid, 'timestamp' => $timestamp, 'apikey' => $apikey, 'secretkey' => $secretkey, 'sig' => $sig));
    // our curl handle (initialize if required)
    $ch = crypto_curl_init();
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; 796 PHP client; ' . php_uname('s') . '; PHP/' . phpversion() . ')');
    curl_setopt($ch, CURLOPT_URL, crypto_wrap_url($token_url));
    // curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    // run the query
    $res = curl_exec($ch);
    if ($res === false) {
        throw new ExternalAPIException('Could not get reply: ' . curl_error($ch));
    }
    $dec = crypto_json_decode($res, "in authentication");
    if (isset($dec['errno']) && $dec['errno']) {
        throw new ExternalAPIException("Could not get OAuth Token: " . htmlspecialchars($dec['msg']));
    }
    if (!isset($dec['data']['access_token'])) {
        throw new ExternalAPIException("No access token provided");
    }
    $token = $dec['data']['access_token'];
    crypto_log("Obtained OAuth token");
    // now, call the given URL
    // 796 has a bug where the token can't be urlencoded again, so we can't use url_add() (though we should)
    $destination_url = $url . "?access_token=" . $token;
    // our curl handle (initialize if required)
    $ch = crypto_curl_init();
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; 796 PHP client; ' . php_uname('s') . '; PHP/' . phpversion() . ')');
    curl_setopt($ch, CURLOPT_URL, crypto_wrap_url($destination_url));
    // curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    // run the query
    $res = curl_exec($ch);
    if ($res === false) {
        throw new ExternalAPIException('Could not get reply: ' . curl_error($ch));
    }
    $dec = crypto_json_decode($res, "in request");
    if (isset($dec['errno']) && $dec['errno']) {
        throw new ExternalAPIException("Error in reply: " . htmlspecialchars($dec['msg']));
    }
    if (!isset($dec['data'])) {
        throw new ExternalAPIException("No data in reply");
    }
    return $dec['data'];
}
Exemple #2
0
 function callback($data)
 {
     $table = $this->table;
     $query = array();
     $args = array();
     foreach ($data as $key => $value) {
         $query[] = $key . " = :" . $key;
         $args[$key] = $value;
     }
     $args["id"] = $this->account['id'];
     crypto_log("Self-updating table '{$table}'");
     $q = db()->prepare("UPDATE {$table} SET " . implode(", ", $query) . " WHERE id=:id");
     $q->execute($args);
 }
Exemple #3
0
function delete_user($id)
{
    $user = get_user($id);
    if (!$user) {
        throw new Exception("No such user {$id}");
    }
    crypto_log("Deleting user " . ($user ? htmlspecialchars(print_r($user, true)) : "<i>(phantom)</i>"));
    // go through all accounts
    $already_done = array();
    foreach (account_data_grouped() as $label => $accounts) {
        foreach ($accounts as $key => $account) {
            if ($account['table'] != 'graphs' && !isset($already_done[$account['table']])) {
                delete_from($account['table']);
                $already_done[$account['table']] = 1;
            }
        }
    }
    delete_from('balances');
    delete_from('address_balances');
    delete_from('hashrates');
    delete_from('securities');
    delete_from('offsets');
    delete_from('summary_instances');
    delete_from('summaries');
    delete_from('graph_data_summary');
    delete_from('graph_data_balances');
    delete_from('pending_subscriptions');
    // delete graphs
    crypto_log("Deleting graphs...");
    $q = db()->prepare("SELECT * FROM graph_pages WHERE user_id=?");
    $q->execute(array($user['id']));
    $pages = $q->fetchAll();
    foreach ($pages as $page) {
        $q = db()->prepare("DELETE FROM graphs WHERE page_id=?");
        $q->execute(array($page['id']));
        crypto_log("(" . number_format($q->rowCount()) . " rows deleted)");
    }
    delete_from('graph_pages');
    delete_from('managed_graphs');
    crypto_log("Deleting user_properties...");
    $q = db()->prepare("DELETE FROM user_properties WHERE id=?");
    $q->execute(array($user['id']));
    crypto_log("(" . number_format($q->rowCount()) . " rows deleted)");
    // finally delete the user object
    crypto_log("Deleting user...");
    $user = Users\User::findUser(db(), $user['id']);
    $user->delete(db());
}
Exemple #4
0
<?php

/**
 * An existing free user has not logged in within X days and we
 * now need to disable their account.
 */
// get the relevant user info
$user = get_user($job['arg_id']);
if (!$user) {
    throw new JobException("Cannot find user ID " . $job['arg_id']);
}
// check that they're not a premium user etc - this should never happen
if ($user['is_premium']) {
    throw new JobException("Premium user was requested to be disabled - this should not happen");
}
// update user (before sending email)
$q = db()->prepare("UPDATE user_properties SET is_disabled=1,disabled_at=NOW() WHERE id=? LIMIT 1");
$q->execute(array($user['id']));
// construct email
if ($user['email']) {
    $disables_at = strtotime(($user['last_login'] ? $user['last_login'] : $user['created_at']) . " +" . get_site_config('user_expiry_days') . " day");
    send_user_email($user, "disable", array("name" => $user['name'] ? $user['name'] : $user['email'], "days" => number_format(get_site_config('user_expiry_days')), "disables" => iso_date($disables_at), "disables_text" => recent_format($disables_at, false, ""), "url" => absolute_url(url_for("user#user_premium")), "login" => absolute_url(url_for("login")), "profile" => absolute_url(url_for("profile"))));
    crypto_log("Sent disabled account e-mail to " . htmlspecialchars($user['email']) . ".");
} else {
    crypto_log("User had no valid e-mail address.");
}
Exemple #5
0
<?php

/**
 * Generic API job.
 */
$exchange = "generic";
// get the relevant address
$q = db()->prepare("SELECT * FROM accounts_generic WHERE user_id=? AND id=?");
$q->execute(array($job['user_id'], $job['arg_id']));
$account = $q->fetch();
if (!$account) {
    throw new JobException("Cannot find a {$exchange} account " . $job['arg_id'] . " for user " . $job['user_id']);
}
$balance = crypto_get_contents(crypto_wrap_url($account['api_url']));
if (!is_numeric($balance)) {
    crypto_log("{$exchange} balance for " . htmlspecialchars($account['api_url']) . " is non-numeric: " . htmlspecialchars($balance));
    throw new ExternalAPIException("Generic API returned non-numeric balance: " . htmlspecialchars(substr($balance, 0, 100)));
} else {
    // issue #11: add editable multiplier
    crypto_log("{$exchange} balance: {$balance} * " . $account['multiplier']);
    $balance = $balance * $account['multiplier'];
    crypto_log("{$exchange} balance for " . htmlspecialchars($account['api_url']) . ": " . $balance);
}
insert_new_balance($job, $account, $exchange, $account['currency'], $balance);
Exemple #6
0
}
require __DIR__ . "/_havelock.php";
$content = havelock_query("https://www.havelockinvestments.com/r/balance", array('key' => $account['api_key']));
// balance, balanceavailable, balanceescrow
$wallet = $content['balance']['balance'];
$balance = 0;
crypto_log("{$exchange} wallet balance for " . $job['user_id'] . ": " . $wallet);
// set is_recent=0 for all old security instances for this user
$q = db()->prepare("UPDATE securities SET is_recent=0 WHERE user_id=? AND exchange=? AND account_id=?");
$q->execute(array($job['user_id'], $exchange, $account['id']));
// assume we don't need to delay
$content = havelock_query("https://www.havelockinvestments.com/r/portfolio", array('key' => $account['api_key']));
if ($content['portfolio'] && is_array($content['portfolio'])) {
    foreach ($content['portfolio'] as $entry) {
        // the API returns the marketvalue, so we can just use that rather than calculate it from previous jobs (like btct)
        crypto_log("{$exchange} security balance for " . htmlspecialchars($entry['symbol']) . ": " . $entry['quantity'] . '*' . $entry['lastprice'] . "=" . $entry['marketvalue']);
        $balance += $entry['marketvalue'];
        // find the security ID, if there is one
        $q = db()->prepare("SELECT * FROM securities_havelock WHERE name=?");
        $q->execute(array($entry['symbol']));
        $security_def = $q->fetch();
        if ($security_def) {
            // insert security instance
            $q = db()->prepare("INSERT INTO securities SET user_id=:user_id, exchange=:exchange, security_id=:security_id, quantity=:quantity, account_id=:account_id, is_recent=1");
            $q->execute(array('user_id' => $job['user_id'], 'exchange' => $exchange, 'security_id' => $security_def['id'], 'quantity' => $entry['quantity'], 'account_id' => $account['id']));
        }
    }
}
// we've now calculated both the wallet balance + the value of all securities
insert_new_balance($job, $account, $exchange . "_wallet", $currency, $wallet);
insert_new_balance($job, $account, $exchange . "_securities", $currency, $balance);
 * so we will have historical data even if no user has the security yet.
 */
$exchange = "securities_litecoininvest";
$currency = 'ltc';
// get the API data
$json = crypto_json_decode(crypto_get_contents(crypto_wrap_url('https://www.litecoininvest.com/api/ticker')));
foreach ($json as $security => $data) {
    crypto_log("Parsing security '" . htmlspecialchars($security) . "'");
    // we now have a new value
    $balance = $data['bid'];
    // also available: ask, latest, outstanding, 24h_vol, etc
    // if this security has a balance of 0, then it's worthless and it's not really
    // worth saving into the database
    if ($balance == 0) {
        crypto_log("Security '" . htmlspecialchars($security) . "' had a bid of 0: ignoring");
        continue;
    }
    $q = db()->prepare("SELECT * FROM securities_litecoininvest WHERE name=?");
    $q->execute(array($security));
    $security_def = $q->fetch();
    if (!$security_def) {
        // need to insert a new security definition, so we can later get its value
        // we can't calculate the value of this security yet
        crypto_log("No securities_litecoininvest definition existed for '" . htmlspecialchars($security) . "': adding in new definition");
        $q = db()->prepare("INSERT INTO securities_litecoininvest SET name=?");
        $q->execute(array($security));
        $security_def = array('name' => $security, 'id' => db()->lastInsertId());
    }
    // since we already have bid data here, we might as well save it for free
    insert_new_balance($job, $security_def, $exchange, $currency, $balance);
}
        $wizard = get_wizard_account_type($account['wizard']);
        if (!$wizard['transaction_creation']) {
            continue;
        }
        $q = db()->prepare("SELECT * FROM " . $account['table'] . " WHERE user_id=?" . (isset($account['query']) ? $account['query'] : false));
        $q->execute(array($job['user_id']));
        while ($a = $q->fetch()) {
            $a['exchange'] = $exchange;
            $current_accounts[$exchange . " " . $a['id']] = $a;
        }
    }
}
crypto_log("User " . $job['user_id'] . " has " . count($current_accounts) . " accounts to parse.");
// are there any creators that need to be deleted for this user?
$q = db()->prepare("SELECT * FROM transaction_creators WHERE user_id=?");
$q->execute(array($job['user_id']));
$to_delete = array();
while ($a = $q->fetch()) {
    if (!isset($current_accounts[$a['exchange'] . " " . $a['account_id']])) {
        $to_delete[] = $a['id'];
    } else {
        unset($current_accounts[$a['exchange'] . " " . $a['account_id']]);
    }
}
if ($to_delete) {
    crypto_log("Need to delete " . count($to_delete) . " old transaction creators");
    $q = db()->prepare("DELETE FROM transaction_creators WHERE user_id=? AND id IN (" . implode(",", $to_delete) . ")");
    $q->execute(array($job['user_id']));
}
crypto_log("Complete.");
Exemple #9
0
                        $args += array("label" => "total " . get_currency_abbr($currency));
                    } else {
                        if (substr($account['summary_type'], 0, strlen('all2')) == 'all2') {
                            $summary_type = substr($account['summary_type'], strlen('all2'));
                            $summary_types = get_total_conversion_summary_types();
                            $args += array("label" => "converted " . $summary_types[$summary_type]['short_title']);
                        } else {
                            throw new JobException("Unknown summary_instance summary_type '" . htmlspecialchars($account['summary_type']) . "'");
                        }
                    }
                }
                $args['label_uc'] = capitalize($args['label']);
                break;
            default:
                throw new JobException("Unknown notification type for email '" . $notification['notification_type'] . "'");
        }
        send_user_email($user, $email_template, $args);
        crypto_log("Sent notification e-mail to " . htmlspecialchars($user['email']) . ".");
        // update user stats
        $q = db()->prepare("UPDATE user_properties SET notifications_sent=notifications_sent+1 WHERE id=?");
        $q->execute(array($user['id']));
    }
    // update the notification
    $q = db()->prepare("UPDATE notifications SET is_notified=1,last_notification=NOW(),last_value=?,notifications_sent=notifications_sent+1 WHERE id=?");
    $q->execute(array($current_value, $notification['id']));
} else {
    crypto_log("Trigger not successful.");
    // update the notification
    $q = db()->prepare("UPDATE notifications SET is_notified=0,last_value=? WHERE id=?");
    $q->execute(array($current_value, $notification['id']));
}
 /**
  * @param $block may be {@code null}
  * @throws {@link BalanceException} if something happened and the balance could not be obtained.
  */
 function getBalanceAtBlock($address, $block = null, Logger $logger, $is_received = false)
 {
     $code = $this->currency->getCode();
     if ($is_received) {
         $logger->info("We are looking for received balance.");
     }
     // do we have a block count?
     if ($this->currency instanceof BlockCurrency && !$block) {
         // TODO this needs to be cacheable between requests, otherwise we're going to end
         // up spamming services for block counts!
         $logger->info("Finding most recent block count...");
         $block = $this->currency->getBlockCount($logger) - $this->confirmations;
     }
     $logger->info("Ignoring blocks after block " . number_format($block));
     // we can now request the HTML page
     $url = sprintf($this->url, $address);
     $logger->info($url);
     try {
         $html = Fetch::get($url);
     } catch (\Apis\FetchHttpException $e) {
         // don't return raw HTML if we can find a valid error message
         if (strpos($e->getContent(), "Not a valid address")) {
             throw new BalanceException("Not a valid address", $e);
         }
         if (strpos($e->getContent(), "Address not seen on the network")) {
             throw new BalanceException("Address not seen on the network", $e);
         }
         // otherwise, throw HTML as normal
         throw new BalanceException($e->getContent(), $e);
     }
     $html = $this->stripHTML($html);
     // assumes that the page format will not change
     if (!$is_received && preg_match('#(<p>|<tr><th>|<tr><td>)Balance:?( |</th><td>|</td><td>)([0-9,\\.]+) ' . $this->currency->getAbbr() . '#im', $html, $matches)) {
         $balance = str_replace(",", "", $matches[3]);
         $logger->info("Address balance before removing unconfirmed: " . $balance);
         // transaction, block, date, amount, [balance,] currency
         if (preg_match_all('#<tr><td>.+</td><td><a href=[^>]+>([0-9]+)</a></td><td>.+?</td><td>(- |\\+ |)([0-9,\\.\\(\\)]+)</td>(|<td>([0-9\\.]+)</td>)<td>' . $this->currency->getAbbr() . '</td></tr>#im', $html, $matches, PREG_SET_ORDER)) {
             foreach ($matches as $match) {
                 if ($match[1] >= $block) {
                     // too recent
                     $amount = str_replace(",", "", $match[3]);
                     if (substr($amount, 0, 1) == "(" && substr($amount, -1) == ")") {
                         // convert (1.23) into -1.23
                         $amount = -substr($amount, 1, strlen($amount) - 2);
                     }
                     if ($match[2] == "+ ") {
                         $amount = +$amount;
                     } else {
                         if ($match[2] == "- ") {
                             $amount = -$amount;
                         }
                     }
                     $logger->info("Removing " . $amount . " from balance: unconfirmed (block " . $match[1] . " >= " . $block . ")");
                     $balance -= $amount;
                 }
             }
             $logger->info("Confirmed balance after " . $this->confirmations . " confirmations: " . $balance);
         } else {
             $this->foundNoTransactions($logger);
         }
     } else {
         if ($is_received && preg_match('#(|<tr><th>|<tr><td>)Received:?( |</th><td>|</td><td>)([0-9,\\.]+) ' . $this->currency->getAbbr() . '#i', $html, $matches)) {
             $balance = str_replace(",", "", $matches[3]);
             $logger->info("Address received before removing unconfirmed: " . $balance);
             // transaction, block, date, amount, [balance,] currency
             if (preg_match_all('#<tr><td>.+</td><td><a href=[^>]+>([0-9]+)</a></td><td>.+?</td><td>(- |\\+ |)([0-9,\\.\\(\\)]+)</td>(|<td>([0-9\\.]+)</td>)<td>' . $this->currency->getAbbr() . '</td></tr>#im', $html, $matches, PREG_SET_ORDER)) {
                 foreach ($matches as $match) {
                     if ($match[1] >= $block) {
                         // too recent
                         $amount = str_replace(",", "", $match[3]);
                         if (substr($amount, 0, 1) == "(" && substr($amount, -1) == ")") {
                             // convert (1.23) into -1.23
                             $amount = -substr($amount, 1, strlen($amount) - 2);
                         }
                         if ($match[2] == "+ ") {
                             $amount = +$amount;
                         } else {
                             if ($match[2] == "- ") {
                                 $amount = -$amount;
                             }
                         }
                         // only consider received
                         if ($amount > 0) {
                             $logger->info("Removing " . $amount . " from received: unconfirmed (block " . $match[1] . " >= " . $block . ")");
                             $balance -= $amount;
                         }
                     }
                 }
                 $logger->info("Confirmed received after " . $this->confirmations . " confirmations: " . $balance);
             } else {
                 $this->foundNoTransactions($logger);
             }
         } else {
             if (strpos($html, "Address not seen on the network.") !== false) {
                 // the address is valid, it just doesn't have a balance
                 $balance = 0;
                 $logger->info("Address is valid, but not yet seen on network");
             } else {
                 if (strpos($html, "Not a valid address.") !== false || strpos($html, "Please enter search terms") !== false) {
                     // the address is NOT valid
                     throw new BalanceException("Not a valid address");
                 } else {
                     if (strpos($html, "this address has too many records to display") !== false) {
                         // this address is valid, and it has a balance, but it has too many records for this Abe instance
                         crypto_log("Address is valid, but has too many records to display");
                         throw new BalanceException("Address has too many transactions");
                     } else {
                         if (strpos(strtolower($html), "500 internal server error") !== false) {
                             crypto_log("Server returned 500 Internal Server Error");
                             throw new BalanceException("Server returned 500 Internal Server Error");
                         } else {
                             throw new BalanceException("Could not find balance on page");
                         }
                     }
                 }
             }
         }
     }
     return $balance;
 }
    // update as necessary
    $stored[$date][$user_id][$exchange][$account_id][$currency]['min'] = min($balance['balance'], $stored[$date][$user_id][$exchange][$account_id][$currency]['min']);
    $stored[$date][$user_id][$exchange][$account_id][$currency]['max'] = max($balance['balance'], $stored[$date][$user_id][$exchange][$account_id][$currency]['max']);
    $stored[$date][$user_id][$exchange][$account_id][$currency]['close'] = $balance['balance'];
    $stored[$date][$user_id][$exchange][$account_id][$currency]['samples']++;
    $stored[$date][$user_id][$exchange][$account_id][$currency]['values'][] = $balance['balance'];
}
crypto_log("Processed " . number_format($count) . " balances entries");
// we now have lots of data; insert it
// danger! danger! five nested loops!
$insert_count = 0;
foreach ($stored as $date => $a) {
    foreach ($a as $user_id => $b) {
        foreach ($b as $exchange => $c) {
            foreach ($c as $account_id => $d) {
                foreach ($d as $currency => $summary) {
                    $q = db_master()->prepare("INSERT INTO graph_data_balances SET\n              user_id=:user_id, exchange=:exchange, account_id=:account_id, currency=:currency, data_date=:data_date, samples=:samples,\n              balance_min=:min, balance_opening=:open, balance_closing=:close, balance_max=:max, balance_stdev=:stdev");
                    $q->execute(array('user_id' => $user_id, 'exchange' => $exchange, 'account_id' => $account_id, 'currency' => $currency, 'data_date' => $date, 'samples' => $summary['samples'], 'min' => $summary['min'], 'open' => $summary['open'], 'close' => $summary['close'], 'max' => $summary['max'], 'stdev' => stdev($summary['values'])));
                    $insert_count++;
                }
            }
        }
    }
}
crypto_log("Inserted " . number_format($insert_count) . " balances entries into graph_data_balances");
// finally, delete all the old data
// we've exhausted over everything so this should be safe
$q = db_master()->prepare("DELETE FROM balances WHERE created_at <= :date ORDER BY created_at ASC");
$q->execute(array("date" => $cutoff_date));
crypto_log("Deleted " . number_format($count) . " summary entries");
batch_footer();
Exemple #12
0
            add_summary_instance($job, 'crypto2' . $currency, $total);
        }
    }
    crypto_log("</ul>");
}
// update last_sum_job
$q = db()->prepare("UPDATE user_properties SET last_sum_job=NOW() WHERE id=?");
$q->execute(array($job['user_id']));
// and now that we have added summary instances, check for first_report
// (this is so that first_report jobs don't block up the job queue)
/**
 * Send an e-mail to new users once their first non-zero summary reports have been compiled.
 */
// reload user in case multiple summary jobs for the same user are all blocked at once
$user = get_user($job['user_id']);
if (!$user['is_first_report_sent']) {
    // is there a non-zero summary instance?
    $q = db()->prepare("SELECT * FROM summary_instances WHERE user_id=? AND is_recent=1 AND balance > 0 LIMIT 1");
    $q->execute(array($user['id']));
    if ($instance = $q->fetch()) {
        crypto_log("User has a non-zero summary instance.");
        // update that we've reported now
        $q = db()->prepare("UPDATE user_properties SET is_first_report_sent=1,first_report_sent=NOW() WHERE id=?");
        $q->execute(array($user['id']));
        // send email
        if ($user['email']) {
            send_user_email($user, "first_report", array("name" => $user['name'] ? $user['name'] : $user['email'], "url" => absolute_url(url_for("profile")), "login" => absolute_url(url_for("login")), "wizard_currencies" => absolute_url(url_for("wizard_currencies")), "wizard_addresses" => absolute_url(url_for("wizard_accounts_addresses")), "wizard_accounts" => absolute_url(url_for("wizard_accounts")), "wizard_notifications" => absolute_url(url_for("wizard_notifications")), "reports" => absolute_url(url_for("profile")), "premium" => absolute_url(url_for("premium"))));
            crypto_log("Sent first report e-mail to " . htmlspecialchars($user['email']) . ".");
        }
    }
}
<?php

/**
 * A batch script to clean up old jobs.
 * This always executes (no job framework) so it should be used sparingly or as necessary.
 *
 * Arguments (in command line, use "-" for no argument):
 *   $key/1 required the automated key
 */
define('USE_MASTER_DB', true);
// always use the master database for selects!
require __DIR__ . "/../inc/global.php";
require __DIR__ . "/_batch.php";
require_batch_key();
batch_header("Batch cleanup jobs", "batch_cleanup_jobs");
crypto_log("Current time: " . date('r'));
// simply delete all jobs that haven't executed and are over three months old
$q = db_master()->prepare("DELETE FROM jobs WHERE is_executed=1 AND executed_at < DATE_SUB(NOW(), INTERVAL 1 MONTH)");
$q->execute(array());
crypto_log("Deleted old jobs.");
batch_footer();
Exemple #14
0
<?php

/**
 * Openclerk version check job.
 */
$exchange = "version_check";
crypto_log("Local version: " . get_site_config('openclerk_version'));
// call cryptfolio.com to find the latest openclerk.org version
$version = crypto_get_contents(crypto_wrap_url(url_add('https://cryptfolio.com/version', array('absolute_url' => get_site_config('absolute_url'), 'openclerk_version' => get_site_config('openclerk_version')))));
crypto_log("Remote version: " . $version);
if (!$version) {
    throw new ExternalAPIException("Could not retrieve remote Openclerk version");
}
// compare
if (version_compare($version, get_site_config('openclerk_version')) > 0) {
    // delete any unread messages
    $q = db()->prepare("DELETE FROM admin_messages WHERE message_type=? AND is_read=0");
    $q->execute(array('version_check'));
    // and insert a new one
    $q = db()->prepare("INSERT INTO admin_messages SET message_type=?, message=?");
    $q->execute(array('version_check', '<a href="http://openclerk.org">A new version</a> of Openclerk is available: ' . $version));
    crypto_log("Inserted new admin_message.");
}
<?php

/**
 * Restore market average data for a particular day (#457).
 */
crypto_log("Restoring market average data for day " . $job['arg_id']);
$q = db()->prepare("SELECT * FROM ticker WHERE created_at_day=? AND is_daily_data=1 AND exchange <> 'average'");
$q->execute(array($job['arg_id']));
$recents = $q->fetchAll();
crypto_log("Found " . number_format(count($recents)) . " ticker instances to recreate average data");
$exchange = array('name' => 'average');
require __DIR__ . "/_average.php";
// we can now create ticker values as necessary
foreach ($pairs as $pair) {
    if ($pair['total_volume'] > 0) {
        // delete any old average data
        $q = db()->prepare("DELETE FROM ticker WHERE exchange=:exchange AND currency1=:currency1 AND currency2=:currency2 AND created_at_day=TO_DAYS(:date)");
        $q->execute(array("date" => $recents[0]['created_at'], "exchange" => $exchange['name'], "currency1" => $pair['currency1'], "currency2" => $pair['currency2']));
        crypto_log("Deleted any old average data");
        // insert in new ticker value
        $q = db()->prepare("INSERT INTO ticker SET exchange=:exchange, currency1=:currency1, currency2=:currency2, last_trade=:last_trade, bid=:bid, ask=:ask, volume=:volume, job_id=:job_id, is_daily_data=1, created_at=:date, created_at_day=TO_DAYS(:date)");
        $q->execute(array("date" => $recents[0]['created_at'], "exchange" => $exchange['name'], "currency1" => $pair['currency1'], "currency2" => $pair['currency2'], "last_trade" => $pair['total_last_trade'] / $pair['total_volume'], "bid" => $pair['total_volume_bid'] > 0 ? $pair['total_bid'] / $pair['total_volume_bid'] : 0, "ask" => $pair['total_volume_ask'] > 0 ? $pair['total_ask'] / $pair['total_volume_ask'] : 0, "volume" => $pair['total_volume'], "job_id" => $job['id']));
        crypto_log("Inserted in new ticker ID " . db()->lastInsertId());
    }
    // no need to track average market count: this isn't used in historical data
}
foreach ($data as $row) {
    $security = $row['ticker'];
    $bid = $row['bid'];
    // also available: avg_buy_price
    // make sure that a security definition exists
    $q = db()->prepare("SELECT * FROM securities_litecoininvest WHERE name=?");
    $q->execute(array($security));
    $security_def = $q->fetch();
    if (!$security_def) {
        // need to insert a new security definition, so we can later get its value
        // we can't calculate the value of this security yet
        crypto_log("No securities_litecoininvest definition existed for '" . htmlspecialchars($security) . "': adding in new definition");
        $q = db()->prepare("INSERT INTO securities_litecoininvest SET name=?");
        $q->execute(array($security));
        $security_def = array('id' => db()->lastInsertId());
    }
    // insert in a new balance
    $job2 = $job;
    $job2['user_id'] = get_site_config('system_user_id');
    /* need to insert security values as system user, or else they won't be displayed in a graph! */
    insert_new_balance($job2, $security_def, "securities_" . $exchange, $currency, $bid);
    $calculated = $bid * $row['quantity'];
    crypto_log(htmlspecialchars($security) . " @ " . htmlspecialchars($bid) . " x " . number_format($row['quantity']) . " = " . htmlspecialchars($calculated));
    $balance += $calculated;
    // insert security instance
    $q = db()->prepare("INSERT INTO securities SET user_id=:user_id, exchange=:exchange, security_id=:security_id, quantity=:quantity, account_id=:account_id, is_recent=1");
    $q->execute(array('user_id' => $job['user_id'], 'exchange' => $exchange, 'security_id' => $security_def['id'], 'quantity' => $row['quantity'], 'account_id' => $account['id']));
}
// we've now calculated both the wallet balance + the value of all securities
insert_new_balance($job, $account, $exchange . '_wallet', $currency, $wallet);
insert_new_balance($job, $account, $exchange . '_securities', $currency, $balance);
<?php

/**
 * 796 security value job.
 * Retrieves the current 'bid' value for a particular security.
 */
$exchange = "securities_796";
$currency = 'btc';
// get the relevant address
$q = db()->prepare("SELECT * FROM securities_796 WHERE id=?");
$q->execute(array($job['arg_id']));
$account = $q->fetch();
if (!$account) {
    throw new JobException("Cannot find a {$exchange} account " . $job['arg_id']);
}
$content = crypto_get_contents(crypto_wrap_url('http://api.796.com/v3/stock/ticker.html?type=' . urlencode($account['api_name'])));
if (!$content) {
    throw new ExternalAPIException("API returned empty data");
}
$data = crypto_json_decode($content);
// also available: last, high, low, vol, buy, sell
if (!isset($data['ticker']['last'])) {
    throw new ExternalAPIException("External API returned no last price");
}
$balance = $data['ticker']['last'];
crypto_log("Last price for " . htmlspecialchars($account['name']) . ": " . $balance);
insert_new_balance($job, $account, $exchange, $currency, $balance);
Exemple #18
0
 function handle(array $record)
 {
     $message = $record['message'];
     if (is_valid_url($message)) {
         return crypto_wrap_url($message);
     }
     if ($record['level'] >= Logger::WARNING) {
         if ($record['level'] >= Logger::ERROR) {
             $message = "[ERROR] " . $message;
         } else {
             $message = "[Warning] " . $message;
         }
     }
     crypto_log($message);
 }
crypto_log("Summary instance type: " . $account['summary_type'] . " for user " . $notification['user_id']);
// get the most recent value
$q = db()->prepare("SELECT * FROM summary_instances WHERE user_id=:user_id AND summary_type=:summary_type AND is_recent=1 LIMIT 1");
$q->execute(array("user_id" => $notification['user_id'], "summary_type" => $account['summary_type']));
$summary_instance = $q->fetch();
if (!$summary_instance) {
    // TODO maybe support failing for notifications, to disable notifications for e.g. accounts that no longer exist?
    // probably better to make sure that we can never *have* a referenced account that never exists
    throw new JobException("Could not find any recent summary instance values for " . $account['summary_type'] . " for user " . $notification['user_id']);
}
$current_value = $summary_instance['balance'];
// what was the last value?
// may need to generate this if no value exists, but hopefully this only occurs very rarely,
// since this may be a very heavy query
if ($notification['last_value'] === null) {
    crypto_log("No last value found: retrieving");
    // get the query string for this interval
    $periods = get_permitted_notification_periods();
    if (!isset($periods[$notification['period']]['interval'])) {
        throw new JobException("Unknown job period '" . $notification['period'] . "'");
    }
    $period = $periods[$notification['period']]['interval'];
    $q = db()->prepare("SELECT * FROM summary_instances WHERE user_id=:user_id AND summary_type=:summary_type AND created_at <= DATE_SUB(NOW(), {$period}) ORDER BY id DESC LIMIT 1");
    $q->execute(array("user_id" => $notification['user_id'], "summary_type" => $account['summary_type']));
    $last = $q->fetch();
    if (!$last) {
        throw new JobException("Could not find any last values for " . $account['summary_type'] . " for user " . $notification['user_id']);
    }
    $notification['last_value'] = $last['balance'];
}
// other parameters
Exemple #20
0
        $security_def = array('id' => db()->lastInsertId());
    } else {
        if (!isset($balances[$security_def['currency']])) {
            // this allows us to safely ignore securities in other currencies
            crypto_log("Security {$security} is not a currently recognised currency: " . $security_def['currency']);
        } else {
            // the 'balance' for this security is the 'bid'
            $q = db()->prepare("SELECT * FROM balances WHERE exchange=:exchange AND account_id=:account_id AND is_recent=1 LIMIT 1");
            $q->execute(array("exchange" => "securities_cryptostocks", "account_id" => $security_def['id']));
            $security_value = $q->fetch();
            if (!$security_value) {
                // we can't calculate the value of this security yet
                crypto_log("Security " . htmlspecialchars($security) . " does not yet have a calculated value");
            } else {
                $calculated = $security_value['balance'] * $shares;
                crypto_log(htmlspecialchars($security) . " @ " . htmlspecialchars($security_value['balance']) . " x " . number_format($shares) . " = " . htmlspecialchars($calculated) . " " . strtoupper($security_def['currency']));
                $balances[$security_def['currency']] += $calculated;
            }
        }
    }
    // insert security instance
    // but only if we actually have a quantity
    if ($shares != 0) {
        $q = db()->prepare("INSERT INTO securities SET user_id=:user_id, exchange=:exchange, security_id=:security_id, quantity=:quantity, account_id=:account_id, is_recent=1");
        $q->execute(array('user_id' => $job['user_id'], 'exchange' => $exchange, 'security_id' => $security_def['id'], 'quantity' => $shares, 'account_id' => $account['id']));
    }
}
// we've now calculated both the wallet balance + the value of all securities
foreach ($wallets as $currency => $balance) {
    insert_new_balance($job, $account, $exchange . '_wallet', $currency, $balance);
}
Exemple #21
0
// "How long does it take for a page to be generated?"
// "What pages have the most database queries?"
// "What pages spend the most time in PHP as opposed to the database?"
// "How many jobs are running per hour?"
// "What jobs have the most database queries?"
// "Which jobs time out the most?"
// "How many blockchain requests fail?"
// "What jobs take the longest requesting URLs?"
// "How many jobs are being queued at once?"
// "Which queue types take the longest?"
// "What are the most common graph types?"
// "How many ticker graphs are being requested?"
// we've processed all the data we want; delete old metrics data
$q = db_master()->prepare("DELETE FROM performance_metrics_slow_queries");
$q->execute();
$q = db_master()->prepare("DELETE FROM performance_metrics_slow_urls");
$q->execute();
$q = db_master()->prepare("DELETE FROM performance_metrics_repeated_queries");
$q->execute();
$q = db_master()->prepare("DELETE FROM performance_metrics_repeated_urls");
$q->execute();
$q = db_master()->prepare("DELETE FROM performance_metrics_pages");
$q->execute();
$q = db_master()->prepare("DELETE FROM performance_metrics_graphs");
$q->execute();
$q = db_master()->prepare("DELETE FROM performance_metrics_jobs");
$q->execute();
$q = db_master()->prepare("DELETE FROM performance_metrics_queues");
$q->execute();
crypto_log("Deleted old metric data.");
batch_footer();
        $data[$key] = 0;
    }
}
// calculate MySQL statistics
// must request the master database manually!
$q = db_master()->prepare("SHOW GLOBAL STATUS");
$q->execute();
$mysql_mapping = array('mysql_uptime' => 'Uptime', 'mysql_threads' => 'Threads_running', 'mysql_questions' => 'Questions', 'mysql_slow_queries' => 'Slow_queries', 'mysql_opens' => 'Opened_tables', 'mysql_flush_tables' => 'Flush_commands', 'mysql_open_tables' => 'Open_tables', 'mysql_locks_immediate' => 'Table_locks_immediate', 'mysql_locks_blocked' => 'Table_locks_waited');
while ($s = $q->fetch()) {
    if (($pos = array_search($s['Variable_name'], $mysql_mapping)) !== false) {
        $data[$pos] = $s['Value'];
    }
}
$data['disk_free_space'] = disk_free_space('/');
// get system statistics if defined (i.e. not Windows)
$query_extra = "";
if (function_exists('sys_getloadavg')) {
    $top = sys_getloadavg();
    $data['system_load_1min'] = $top[0];
    $data['system_load_5min'] = $top[1];
    $data['system_load_15min'] = $top[2];
    $query_extra = "\n    system_load_1min = :system_load_1min,\n    system_load_5min = :system_load_5min,\n    system_load_15min = :system_load_15min,\n  ";
}
crypto_log(print_r($data, true));
$q = db()->prepare("UPDATE site_statistics SET is_recent=0 WHERE is_recent=1");
$q->execute();
$q = db()->prepare("INSERT INTO site_statistics SET\n  total_users = :total_users,\n  disabled_users = :disabled_users,\n  premium_users = :premium_users,\n  total_emails_sent = :total_emails_sent,\n\n  free_delay_minutes = :free_delay_minutes,\n  premium_delay_minutes = :premium_delay_minutes,\n  outstanding_jobs = :outstanding_jobs,\n  external_status_job_count = :external_status_job_count,\n  external_status_job_errors = :external_status_job_errors,\n\n  mysql_uptime = :mysql_uptime,\n  mysql_threads = :mysql_threads,\n  mysql_questions = :mysql_questions,\n  mysql_slow_queries = :mysql_slow_queries,\n  mysql_opens = :mysql_opens,\n  mysql_flush_tables = :mysql_flush_tables,\n  mysql_open_tables = :mysql_open_tables,\n  mysql_locks_immediate = :mysql_locks_immediate,\n  mysql_locks_blocked = :mysql_locks_blocked,\n\n  users_graphs_managed_none = :users_graphs_managed_none,\n  users_graphs_managed_managed = :users_graphs_managed_managed,\n  users_graphs_managed_auto = :users_graphs_managed_auto,\n  users_graphs_need_update = :users_graphs_need_update,\n  users_subscribe_announcements = :users_subscribe_announcements,\n  pending_subscriptions = :pending_subscriptions,\n  pending_unsubscriptions = :pending_unsubscriptions,\n\n  user_logins_after_warned = :user_logins_after_warned,\n  users_login_after_warned = :users_login_after_warned,\n  user_logins_after_disabled = :user_logins_after_disabled,\n  users_login_after_disabled = :users_login_after_disabled,\n\n  notifications_sent = :notifications_sent,\n  max_notifications_sent = :max_notifications_sent,\n\n  jobs_tests = :jobs_tests,\n  jobs_timeout = :jobs_timeout,\n\n  disk_free_space = :disk_free_space,\n\n  {$query_extra}\n\n  is_recent=1\n  ");
$q->execute($data);
$insert_id = db()->lastInsertId();
crypto_log("Inserted new site statistics ID {$insert_id}");
batch_footer();
<?php

/**
 * Individual Crypto-Trade securities job.
 */
$exchange = "individual_crypto-trade";
// get the relevant address
$q = db()->prepare("SELECT * FROM accounts_individual_cryptotrade WHERE user_id=? AND id=?");
$q->execute(array($job['user_id'], $job['arg_id']));
$account = $q->fetch();
if (!$account) {
    throw new JobException("Cannot find a {$exchange} account " . $job['arg_id'] . " for user " . $job['user_id']);
}
// get the most recent ticker balance for this security
$q = db()->prepare("SELECT * FROM balances WHERE exchange=? AND account_id=? AND is_recent=1 LIMIT 1");
$q->execute(array('securities_crypto-trade', $account['security_id']));
$ticker = $q->fetch();
if (!$ticker) {
    throw new ExternalAPIException("Could not find any recent ticker balance for securities_crypto-trade ID=" . htmlspecialchars($account['security_id']));
} else {
    $calculated = $ticker['balance'] * $account['quantity'];
    crypto_log('security ' . htmlspecialchars($account['security_id']) . " @ " . htmlspecialchars($ticker['balance']) . " x " . number_format($account['quantity']) . " = " . htmlspecialchars($calculated));
    insert_new_balance($job, $account, $exchange, $ticker['currency'], $calculated);
}
<?php

/**
 * Crypto-Trade securities ticker job.
 */
$exchange = "securities_crypto-trade";
// get the relevant security
$q = db()->prepare("SELECT * FROM securities_cryptotrade WHERE id=?");
$q->execute(array($job['arg_id']));
$security = $q->fetch();
if (!$security) {
    throw new JobException("Cannot find a {$exchange} security " . $job['arg_id'] . " for user " . $job['user_id']);
}
$cur1 = $security['currency'];
$cur2 = strtolower($security['name']);
$rates = crypto_json_decode(crypto_get_contents(crypto_wrap_url("https://crypto-trade.com/api/1/ticker/" . $cur2 . "_" . $cur1)));
if (!isset($rates['data']['max_bid'])) {
    if (isset($rates['error'])) {
        throw new ExternalAPIException("Could not find {$cur1}/{$cur2} rate for {$exchange}: " . htmlspecialchars($rates['error']));
    }
    throw new ExternalAPIException("No {$cur1}/{$cur2} rate for {$exchange}");
}
crypto_log("Security {$cur1}/{$cur2} balance: " . $rates['data']['max_bid']);
// insert new balance
insert_new_balance($job, $security, 'securities_crypto-trade', $security['currency'], $rates['data']['max_bid']);
Exemple #25
0
/**
 * @param $currency
 * @param $value a number
 */
function insert_new_difficulty($job, $currency, $value)
{
    $block_table = "difficulty_" . $currency;
    // disable old instances
    $q = db()->prepare("UPDATE {$block_table} SET is_recent=0 WHERE is_recent=1");
    $q->execute();
    // we have a balance; update the database
    $q = db()->prepare("INSERT INTO {$block_table} SET difficulty=:value,is_recent=1");
    $q->execute(array("value" => $value));
    crypto_log("Inserted new {$block_table} id=" . db()->lastInsertId());
}
<?php

/**
 * Havelock Investments security value job.
 * Retrieves the current 'bid' value for a particular security.
 */
$exchange = "securities_havelock";
$currency = 'btc';
// get the relevant address
$q = db()->prepare("SELECT * FROM securities_havelock WHERE id=?");
$q->execute(array($job['arg_id']));
$account = $q->fetch();
if (!$account) {
    throw new JobException("Cannot find a {$exchange} account " . $job['arg_id']);
}
require __DIR__ . "/_havelock.php";
$content = havelock_query("https://www.havelockinvestments.com/r/tickerfull", array('symbol' => $account['name']));
crypto_log("Last price for " . htmlspecialchars($account['name']) . ": " . $content[$account['name']]['last']);
$balance = $content[$account['name']]['last'];
insert_new_balance($job, $account, $exchange, $currency, $balance);
 * is disabled.
 */
// get the relevant user info
$user = get_user($job['arg_id']);
if (!$user) {
    throw new JobException("Cannot find user ID " . $job['arg_id']);
}
// check that they're not a premium user etc - this should never happen
if ($user['is_premium']) {
    throw new JobException("Premium user was requested to be warned of disabled - this should not happen");
} else {
    if ($user['is_disabled']) {
        throw new JobException("Disabled user was requested to be warned of disabled - this should not happen");
    }
}
$disables_at = strtotime(($user['last_login'] ? $user['last_login'] : $user['created_at']) . " +" . get_site_config('user_expiry_days') . " day");
// update user (before sending email)
$q = db()->prepare("UPDATE user_properties SET is_disable_warned=1,disable_warned_at=NOW() WHERE id=? LIMIT 1");
$q->execute(array($user['id']));
if ($disables_at > time()) {
    // there's no point in sending an email if it's going to be disabled in the past; it will be disabled on our very next run anyway
    // construct email
    if ($user['email']) {
        send_user_email($user, "disable_warning", array("name" => $user['name'] ? $user['name'] : $user['email'], "days" => number_format(get_site_config('user_expiry_days')), "disables" => iso_date($disables_at), "disables_text" => recent_format($disables_at, false, ""), "url" => absolute_url(url_for("user#user_premium")), "login" => absolute_url(url_for("login"))));
        crypto_log("Sent disable warning soon e-mail to " . htmlspecialchars($user['email']) . ".");
    } else {
        crypto_log("User had no valid e-mail address.");
    }
} else {
    crypto_log("Did not send any disable warning: disable time is set into the past (" . iso_date($disables_at) . ")");
}
<?php

/**
 * Securities count job - count how many securities a user currently has. This value is
 * displayed on the Profile tabs as Your Securities (X).
 */
// construct a query using
$accounts = account_data_grouped();
$unions = array();
$args = array('user_id' => $job['user_id']);
$count = 0;
foreach ($accounts['Individual Securities'] as $key => $data) {
    $unique = "u" . $count++;
    $unions[] = "(SELECT :" . $unique . "_name AS exchange, security_id FROM " . $data['table'] . " WHERE user_id=:user_id)\n";
    // doesn't directly required an alias
    $args[$unique . '_name'] = $data['exchange'];
}
$query = "SELECT COUNT(*) AS c FROM\n\t(SELECT exchange, security_id FROM (\n\t\t(SELECT exchange, security_id FROM securities WHERE user_id=:user_id AND is_recent=1)\n\t\tUNION " . implode(" UNION ", $unions) . "\n\t) u GROUP BY exchange, security_id) t";
crypto_log("<pre>" . $query . "</pre>");
crypto_log(print_r($args, true));
// execute
$q = db()->prepare($query);
$q->execute($args);
$security_count = $q->fetch();
crypto_log("Securities found for user " . $job['user_id'] . ": " . number_format($security_count['c']));
$q = db()->prepare("UPDATE user_properties SET securities_count=? WHERE id=?");
$q->execute(array($security_count['c'], $job['user_id']));
 /**
  * Implements failing tables; if an account type fails multiple times,
  * then send the user an email and disable the account.
  * @see OpenclerkJobQueuer#getStandardJobs()
  */
 function failed(\Exception $runtime_exception, Connection $db, Logger $logger)
 {
     // is this a standard job?
     $standard = $this->findStandardJob();
     if ($standard) {
         $logger->info("Using standard job " . print_r($standard, true));
         if (!$standard['failure']) {
             $logger->info("Not a failure standard job");
             return;
         }
     } else {
         return;
     }
     $failing_table = $standard['table'];
     $job = $this->job;
     // find the relevant account_data for this standard job
     $account_data = false;
     foreach (account_data_grouped() as $label => $group) {
         foreach ($group as $exchange => $data) {
             if (isset($data['job_type']) && $job['job_type'] == $data['job_type']) {
                 $account_data = $data;
                 $account_data['exchange'] = $exchange;
                 break;
             }
         }
     }
     if (!$account_data) {
         $logger->warn("Could not find any account data for job type '" . $job['job_type'] . "'");
     }
     $logger->info("Using account data " . print_r($account_data, true));
     // don't count CloudFlare as a failure
     if ($runtime_exception instanceof CloudFlareException || $runtime_exception instanceof \Openclerk\Apis\CloudFlareException) {
         $logger->info("Not increasing failure count: was a CloudFlareException");
     } else {
         if ($runtime_exception instanceof IncapsulaException || $runtime_exception instanceof \Openclerk\Apis\IncapsulaException) {
             $logger->info("Not increasing failure count: was a IncapsulaException");
         } else {
             if ($runtime_exception instanceof BlockchainException || $runtime_exception instanceof \Core\BlockchainException) {
                 $logger->info("Not increasing failure count: was a BlockchainException");
             } else {
                 $q = $db->prepare("UPDATE {$failing_table} SET failures=failures+1,first_failure=IF(ISNULL(first_failure), NOW(), first_failure) WHERE id=?");
                 $q->execute(array($job['arg_id']));
                 $logger->info("Increasing account failure count");
             }
         }
     }
     $user = get_user($job['user_id']);
     if (!$user) {
         $logger->info("Warning: No user " . $job['user_id'] . " found");
     } else {
         // failed too many times?
         $q = $db->prepare("SELECT * FROM {$failing_table} WHERE id=? LIMIT 1");
         $q->execute(array($job['arg_id']));
         $account = $q->fetch();
         $logger->info("Current account failure count: " . number_format($account['failures']));
         if ($account['failures'] >= get_premium_value($user, 'max_failures')) {
             // disable it and send an email
             $q = $db->prepare("UPDATE {$failing_table} SET is_disabled=1 WHERE id=?");
             $q->execute(array($job['arg_id']));
             crypto_log(print_r($account_data, true));
             if ($user['email'] && !$account['is_disabled']) {
                 $email_type = $job['job_type'] == "notification" ? "failure_notification" : "failure";
                 send_user_email($user, $email_type, array("name" => $user['name'] ? $user['name'] : $user['email'], "exchange" => get_exchange_name($account_data['exchange']), "label" => $account_data['label'], "labels" => $account_data['labels'], "failures" => number_format($account['failures']), "message" => $runtime_exception->getMessage(), "length" => recent_format(strtotime($account['first_failure']), "", ""), "title" => isset($account['title']) && $account['title'] ? "\"" . $account['title'] . "\"" : "untitled", "url" => absolute_url(url_for("wizard_accounts"))));
                 $logger->info("Sent failure e-mail to " . htmlspecialchars($user['email']) . ".");
             }
         }
     }
 }
Exemple #30
0
 * A batch script to get all current Havelock securities and queue them up for ticker values,
 * so we will have historical data even if no user has the security yet.
 */
$exchange = "securities_havelock";
$currency = 'btc';
// get the API data
$content = crypto_get_contents(crypto_wrap_url('https://www.havelockinvestments.com/r/tickerfull'));
if (!$content) {
    throw new ExternalAPIException("API returned empty data");
}
$json = json_decode($content, true);
if (!$json) {
    throw new ExternalAPIException("JSON was invalid");
}
foreach ($json as $security => $data) {
    // $data only has last price, so we'll let securities_havelock job deal with the bid/ask
    $q = db()->prepare("SELECT * FROM securities_havelock WHERE name=?");
    $q->execute(array($security));
    $security_def = $q->fetch();
    if (!$security_def) {
        // need to insert a new security definition, so we can later get its value
        // we can't calculate the value of this security yet
        crypto_log("No securities_havelock definition existed for '" . htmlspecialchars($security) . "': adding in new definition");
        $q = db()->prepare("INSERT INTO securities_havelock SET name=?");
        $q->execute(array($security));
        $security_def = array('name' => $security, 'id' => db()->lastInsertId());
    }
    $balance = $data['last'];
    // since we already have last price here, we might as well save it for free
    insert_new_balance($job, $security_def, $exchange, $currency, $balance);
}