├── .gitignore ├── LICENSE ├── README.md ├── balance_over_time.php ├── bootstrap.php ├── composer.json ├── composer.lock ├── functions.php ├── src └── SteemTools │ ├── Bots │ └── CopyCatVoter.php │ ├── Library │ └── steemd.php │ ├── Reporting │ ├── ExchangeTransfers.php │ └── FollowerStats.php │ ├── SteemAPI.php │ └── SteemServiceLayer.php ├── steem_rate.php └── tests ├── Bots ├── CopyCatVoterTest.php └── CopyCatVoterTestCurationReport.php ├── Reporting ├── ExchangeTransfersTest.php ├── FollowerStatsTest.php ├── FollowerWitnessVotes.php ├── comments.php ├── promotions.php ├── steemmonsters.php ├── steemmonsters_sales.php ├── steemmonsters_sales_breakdown.php └── votes.php └── SteemAPITest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 lukestokes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-steem-tools 2 | Various tools and scripts written in PHP for exploring the STEEM blockchain. 3 | 4 | *2016-08-14:* 5 | This code is still under development. Today I started putting things into a modern structure with composer to include a service layer and an SteemAPI class. I'll continue cleaning things up and add some tests as time permits. 6 | 7 | ## Reports 8 | 9 | FollowerStats: Get the top 100 followed users. 10 | 11 | ExchangeTransfers: Get the 50 accounts transfering to and from an exchange along with a daily report of exchange transfer activity. 12 | 13 | ## Bots 14 | 15 | CopyCatVoter: A bot for tracking the votes of another account and voting on the same content after they vote. 16 | 17 | ## Steem Rate: Interest Rate Calculator for Steem Power 18 | 19 | A quick little script I put together for looking at the Steem Power Interest rate and how it changes over time. 20 | 21 | To run it: 22 | 23 | ``` 24 | php steem_rate.php 25 | ``` 26 | Where is the Steem username you want to look into. For more debugging output, include a `true` parmeter: 27 | 28 | ``` 29 | php steem_rate.php true 30 | ``` 31 | Example: 32 | 33 | ``` 34 | ➜ php-steem-tools git:(master) ✗ php steem_rate.php dantheman true 35 | Getting exchange rate at 2016-07-12T11:55:50-05:00... 36 | Rate: 1 SP = 1M VESTS = 211.18176472117 37 | Getting VESTS balance for dantheman via Piston... 38 | VESTS: 5916182088.009379... 39 | 1468342552: Starting Steem Power balance: 1249389.7737576 40 | Sleeping 1 minute... 41 | . 42 | Getting exchange rate at 2016-07-12T11:56:52-05:00... 43 | Rate: 1 SP = 1M VESTS = 211.18302197611 44 | Getting VESTS balance for dantheman via Piston... 45 | VESTS: 5916182088.009379... 46 | 1468342614: Ending Steem Power balance: 1249397.2119068 47 | -------------------------------- 48 | Interest Rate Per Hour: 0.0357% 49 | Steem Power Per Hour: 446.28894975409 50 | Interest Rate Per Week: 5.9976% 51 | Steem Power Per Week: 74976.543558687 52 | ``` 53 | 54 | Note: If any other activity is going on within the account during the time you run the test, you won't get accurate results reflecting *just* the changes due to interest rates. 55 | 56 | ## Steem Power Balance Over Time 57 | 58 | A simple tool for creating a text file of your change in Steem Power with one line added to the file every 5 minutes. 59 | 60 | ``` 61 | php balance_over_time.php 62 | ``` 63 | It will output a file like so: 64 | 65 | ![](http://content.screencast.com/users/lukestokes/folders/Jing/media/bb59190e-c2be-47fa-9906-ef19e234fe48/00002266.png) 66 | 67 | ### Requirements: 68 | 69 | * PHP with Curl 70 | * [Piston](http://piston.readthedocs.io/en/develop/index.html) (only needed for non-composer code) 71 | -------------------------------------------------------------------------------- /balance_over_time.php: -------------------------------------------------------------------------------- 1 | =5.3.4" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "4.8.*" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-0": { 33 | "JsonRPC": "src/" 34 | } 35 | }, 36 | "notification-url": "https://packagist.org/downloads/", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Frédéric Guillot" 43 | } 44 | ], 45 | "description": "Simple Json-RPC client/server library that just works", 46 | "homepage": "https://github.com/fguillot/JsonRPC", 47 | "time": "2016-06-25 23:11:10" 48 | } 49 | ], 50 | "packages-dev": [], 51 | "aliases": [], 52 | "minimum-stability": "stable", 53 | "stability-flags": { 54 | "fguillot/json-rpc": 0 55 | }, 56 | "prefer-stable": false, 57 | "prefer-lowest": false, 58 | "platform": [], 59 | "platform-dev": [] 60 | } 61 | -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | result->total_vesting_fund_steem / $result_json->result->total_vesting_shares; 43 | $exchange_rate *= 1000000; 44 | if ($debug) { 45 | print " Rate: 1 SP = 1M VESTS = " . $exchange_rate . "\n"; 46 | } 47 | /* 48 | Previous approach to steemd is no good because these values aren't dynamic enough. 49 | 50 | $steemd_distribution = file_get_contents('https://steemd.com/distribution'); 51 | $pattern = "%1 SP = 1M VESTS = ([\d\.]*) STEEM = \\$([\d\.]*)<\/code>%"; 52 | $matches = array(); 53 | preg_match($pattern, $steemd_distribution, $matches); 54 | $exchange_rate = $matches[1]; 55 | print " Rate: 1 SP = 1M VESTS = " . $exchange_rate . "\n"; 56 | */ 57 | 58 | return $exchange_rate; 59 | } -------------------------------------------------------------------------------- /src/SteemTools/Bots/CopyCatVoter.php: -------------------------------------------------------------------------------- 1 | SteemAPI = $SteemAPI; 26 | if (array_key_exists('follow_vote_comments', $config)) { 27 | $this->follow_vote_comments = $config['follow_vote_comments']; 28 | } 29 | if (array_key_exists('looptime_in_seconds', $config)) { 30 | $this->looptime_in_seconds = $config['looptime_in_seconds']; 31 | } 32 | if (array_key_exists('reminder_in_seconds', $config)) { 33 | $this->reminder_in_seconds = $config['reminder_in_seconds']; 34 | } 35 | if (array_key_exists('auto_vote', $config)) { 36 | $this->auto_vote = $config['auto_vote']; 37 | } 38 | if (array_key_exists('delay_in_minutes_to_vote_post_by_followed_author', $config)) { 39 | $this->delay_in_minutes_to_vote_post_by_followed_author = $config['delay_in_minutes_to_vote_post_by_followed_author']; 40 | } 41 | } 42 | 43 | public function hasNewVote($account) 44 | { 45 | $changed = false; 46 | $votes = $this->SteemAPI->getAccountVotes(array($account)); 47 | $last_vote = null; 48 | foreach($votes as $vote) { 49 | $include_vote = true; 50 | if ($vote['percent'] != 10000 && $vote['percent'] != 0) { 51 | $include_vote = false; 52 | } 53 | if (!$this->follow_vote_comments && $this->isCommentVote($vote)) { 54 | $include_vote = false; 55 | } 56 | if ($include_vote) { 57 | $timestamp = strtotime($vote['time']); 58 | if ($this->max_timestamp < $timestamp) { 59 | $last_vote = $vote; 60 | $this->max_timestamp = $timestamp; 61 | } 62 | } 63 | } 64 | if ($last_vote) { 65 | if ($this->last_vote != $last_vote) { 66 | $changed = true; 67 | $this->last_vote = $last_vote; 68 | $this->updateContentOfLastVote(); 69 | } 70 | } 71 | return $changed; 72 | } 73 | 74 | public function updateContentOfLastVote() 75 | { 76 | $author_and_link = $this->SteemAPI->getAuthorAndPermLink($this->last_vote['authorperm']); 77 | $this->last_content = $this->SteemAPI->getContent( 78 | array( 79 | $author_and_link['author'], 80 | $author_and_link['permlink'] 81 | ) 82 | ); 83 | } 84 | 85 | public function isCommentVote($vote) 86 | { 87 | return (strpos($vote['authorperm'], '/re-') !== false); 88 | } 89 | 90 | public function hasVoted($account, $refresh_content = false) 91 | { 92 | if ($refresh_content) { 93 | $this->updateContentOfLastVote(); 94 | } 95 | foreach ($this->last_content['active_votes'] as $active_vote) { 96 | if ($active_vote['voter'] == $account) { 97 | return true; 98 | } 99 | } 100 | return false; 101 | } 102 | 103 | public function run($voter_account, $account_to_copy) 104 | { 105 | print date('c') . "\n"; 106 | print "Starting CopyCatVoter for $voter_account who wants to copy $account_to_copy...\n\n"; 107 | $time = time(); 108 | while(true) { 109 | if ($this->hasNewVote($account_to_copy)) { 110 | print "\n------------ NEW VOTE! -----------\n"; 111 | print "https://steemit.com" . $this->last_content['url'] . "\n"; 112 | print $this->last_vote['time'] . "\n"; 113 | print "----------------------------------\n"; 114 | if (!$this->hasVoted($voter_account)) { 115 | if ($this->auto_vote) { 116 | $weight = 100; 117 | if ($this->last_vote['percent'] == 0) { 118 | $weight = 0; 119 | print "**** UNOVOTING ****\n"; 120 | } else { 121 | print "Voting...\n"; 122 | if ($this->last_content['author'] == $account_to_copy && $this->last_content['depth'] == 0) { 123 | print "Looks like this is a new root post by the account we're copying.\n"; 124 | print "Waiting " . $this->delay_in_minutes_to_vote_post_by_followed_author . " minutes to vote.\n"; 125 | for($i = 0; $i < $this->delay_in_minutes_to_vote_post_by_followed_author; $i++) { 126 | print "Voting in " . ($this->delay_in_minutes_to_vote_post_by_followed_author - $i) . " minutes...\n"; 127 | sleep(60); 128 | } 129 | } 130 | } 131 | $this->vote($voter_account, $this->last_vote['authorperm'], $weight); 132 | } else { 133 | print "\n"; 134 | print "Go vote for https://steemit.com" . $this->last_content['url'] . "\n"; 135 | print "\n"; 136 | } 137 | } else { 138 | print "Already voted.\n"; 139 | } 140 | } else { 141 | if (!$this->auto_vote && (time() - $time) >= $this->reminder_in_seconds) { 142 | if (!$this->hasVoted($voter_account,true)) { 143 | print "\n"; 144 | print "REMINDER: Go vote for https://steemit.com" . $this->last_content['url'] . "\n"; 145 | print "\n"; 146 | } 147 | $time = time(); 148 | } 149 | } 150 | print '.'; 151 | sleep($this->looptime_in_seconds); 152 | } 153 | } 154 | 155 | /* 156 | * This method uses the command line piston and must have a posting key with no wallet password. 157 | */ 158 | public function vote($voter, $permlink, $weight = 100) 159 | { 160 | $command = 'piston upvote --voter ' . $voter; 161 | $command .= ' --weight ' . $weight; 162 | $command .= ' ' . $permlink; 163 | $output = array(); 164 | $return = array(); 165 | $result = exec($command, $output, $return); 166 | /* 167 | var_dump($output); 168 | var_dump($return); 169 | var_dump($result); 170 | */ 171 | file_put_contents('copy_cat_voter_history.txt', '"' . date('c') . '","' . $permlink . "\"\n", FILE_APPEND); 172 | } 173 | 174 | public function currationReport($account) 175 | { 176 | $currations = array(); 177 | $file = @fopen("copy_cat_voter_history.txt","r"); 178 | $count = 0; 179 | if ($file) { 180 | while(!feof($file)) { 181 | $count++; 182 | $line = fgetcsv($file); 183 | $currations[trim($line[1])] = array('time' => trim($line[0]), 'permlink' => trim($line[1]), 'curate_reward' => 0.0); 184 | } 185 | fclose($file); 186 | } 187 | 188 | $start = date('c',strtotime('last week')); 189 | $end = date('c'); 190 | $curration_rewards = $this->SteemAPI->getAccountHistoryFiltered($account, array('curate_reward'),$start,$end); 191 | $curration_rewards = $this->SteemAPI->getOpData($curration_rewards); 192 | 193 | foreach ($curration_rewards as $curration_reward) { 194 | $key = $curration_reward['comment_author'] . '/' . $curration_reward['comment_permlink']; 195 | if (array_key_exists($key, $currations)) { 196 | $currations[$key]['curate_reward'] = $this->SteemAPI->vest2sp(str_replace(' VESTS', '', $curration_reward['reward'])); 197 | } 198 | } 199 | 200 | $total_sp = 0; 201 | $report_string = "Curation Reward (SP),permlink\n"; 202 | foreach ($currations as $curration) { 203 | $line = $curration['curate_reward'] . ',' . $curration['permlink'] . "\n"; 204 | $report_string .= $line; 205 | $total_sp += $curration['curate_reward']; 206 | print $line; 207 | } 208 | 209 | print "\n\nTotal SP Earned by CopCatVoter: " . $total_sp . "\n\n"; 210 | 211 | $filename = 'copy_cat_voter_history_with_curation_rewards_' . date('Y-m-d') . '.txt'; 212 | 213 | file_put_contents($filename, $report_string); 214 | 215 | print "File saved: " . $filename; 216 | } 217 | 218 | } -------------------------------------------------------------------------------- /src/SteemTools/Library/steemd.php: -------------------------------------------------------------------------------- 1 | host = $host; 19 | $httpClient = new HttpClient($host); 20 | $httpClient->withoutSslVerification(); 21 | $this->client = new Client($host, false, $httpClient); 22 | } 23 | 24 | public function getAccountHistory($username, $limit = 100, $skip = -1) 25 | { 26 | $api = $this->getApi('database_api'); 27 | return $this->client->call($api, 'get_account_history', [$username, $skip, $limit]); 28 | } 29 | 30 | public function getAccountVotes($username) 31 | { 32 | $api = $this->getApi('database_api'); 33 | return $this->client->call($api, 'get_account_votes', [$username]); 34 | } 35 | 36 | public function getContent($author, $permlink) 37 | { 38 | $api = $this->getApi('database_api'); 39 | return $this->client->call($api, 'get_content', [$author, $permlink]); 40 | } 41 | 42 | public function getProps() 43 | { 44 | $api = $this->getApi('database_api'); 45 | return $this->client->call($api, 'get_dynamic_global_properties', []); 46 | } 47 | 48 | public function getApi($name) 49 | { 50 | if (!array_key_exists($name, $this->api_ids)) { 51 | $key = $this->client->call(1, 'get_api_by_name', [$name]); 52 | $this->api_ids[$name] = $key; 53 | } 54 | return $this->api_ids[$name]; 55 | } 56 | 57 | public function getFollowing($username, $limit = 100, $skip = -1) 58 | { 59 | $api = $this->getApi('follow_api'); 60 | return $this->client->call($api, 'get_following', [$username, $skip, $limit]);; 61 | } 62 | } -------------------------------------------------------------------------------- /src/SteemTools/Reporting/ExchangeTransfers.php: -------------------------------------------------------------------------------- 1 | array('hitbtc-exchange','hitbtc-payout'), 'freewallet.org' => array('freewallet.org','freewallet'), 'openledger' => array('openledger','openledger-dex'), 'binance' => array('binance-hot','deepcrypto8'), 'upbit' => array('upbit-exchange','myupbit','upbitsteemhot','upbituserwallet'), 'gopax' => array('gopax','gopax-deposit'), 'huobi' => array('huobi-pro','huobi-withdrawal'), 'bithumb' => array('bithumb.hot','bithumb.live')); 15 | public $exchange_data = array(); 16 | public $min_timestamp = 0; 17 | public $max_timestamp = 0; 18 | 19 | public function __construct($SteemAPI, $config = array()) 20 | { 21 | $this->SteemAPI = $SteemAPI; 22 | if (array_key_exists('number_of_top_accounts_to_show', $config)) { 23 | $this->number_of_top_accounts_to_show = $config['number_of_top_accounts_to_show']; 24 | } 25 | if (array_key_exists('exchange_accounts', $config)) { 26 | $this->exchange_accounts = $config['exchange_accounts']; 27 | } 28 | $this->min_timestamp = time(); 29 | } 30 | 31 | public function getExchangeData($start, $end) 32 | { 33 | foreach($this->exchange_accounts as $exchange_account) { 34 | if (!array_key_exists($exchange_account, $this->linked_exchange_accounts)) { 35 | $this->getExchangeDataForAccount($exchange_account, $exchange_account, $start, $end); 36 | } else { 37 | foreach ($this->linked_exchange_accounts[$exchange_account] as $actual_account) { 38 | $this->getExchangeDataForAccount($actual_account, $exchange_account, $start, $end); 39 | } 40 | } 41 | 42 | } 43 | return $this->exchange_data; 44 | } 45 | 46 | public function getExchangeDataForAccount($exchange_account, $account_alias, $start, $end) 47 | { 48 | $results = $this->getAccountTransferHistory($exchange_account, $start, $end); 49 | $transfers = $this->getTransfers($results); 50 | $this->exchange_data[$account_alias] = $transfers; 51 | } 52 | 53 | public function getTransfers($result) 54 | { 55 | $transfers = array(); 56 | foreach($result as $block) { 57 | $transfer = $block[1]['op'][1]; 58 | list($amount, $currency) = sscanf($transfer['amount'], "%f %s"); 59 | $transfer['amount'] = $amount; 60 | $transfer['currency'] = $currency; 61 | $is_to_an_exchange = false; 62 | foreach ($this->linked_exchange_accounts as $alias => $accounts) { 63 | foreach ($accounts as $account) { 64 | if ($account == $transfer['from']) { 65 | $transfer['from'] = $alias; 66 | $is_to_an_exchange = true; 67 | } 68 | if ($account == $transfer['to']) { 69 | $transfer['to'] = $alias; 70 | $is_to_an_exchange = true; 71 | } 72 | } 73 | } 74 | 75 | /* 76 | if ($transfer['to'] == "" || $transfer['from'] == "") { 77 | var_dump($transfer); 78 | var_dump($block); 79 | } 80 | 81 | if ($transfer['to'] == "huobi" || $transfer['from'] == "huobi") { 82 | var_dump($transfer); 83 | } 84 | */ 85 | // exclude blocktrade powerups 86 | if ($transfer['to'] != "") { 87 | $transfers[] = $transfer; 88 | } 89 | } 90 | return $transfers; 91 | } 92 | 93 | public function runHourlyReport($start, $end) 94 | { 95 | // print the header 96 | $header = "Date,"; 97 | foreach($this->exchange_accounts as $exchange_account) { 98 | $header .= $exchange_account . ","; 99 | } 100 | $header .= "Total\n"; 101 | 102 | $sbd_report = $header; 103 | $steem_report = $header; 104 | $usd_report = $header; 105 | 106 | $startDate = new \DateTime($start); 107 | $endDate = new \DateTime($end); 108 | $days = $startDate->diff($endDate)->format("%a"); 109 | $price_history = $this->SteemAPI->getPriceHistoryInUSD($start, $end); 110 | 111 | for ($day = 0; $day < $days; $day++) { 112 | for ($hour = 0; $hour < 24; $hour++) { 113 | $currentDate = clone $startDate; 114 | $nextDate = clone $startDate; 115 | $currentDate->add(new \DateInterval('P' . $day . 'DT' . $hour . 'H')); 116 | $nextDate->add(new \DateInterval('P' . ($day+1) . 'DT' . ($hour+1) . 'H')); 117 | 118 | print "Processing " . $currentDate->format('Y-m-d H:i') . "\n"; 119 | 120 | $this->exchange_data = array(); 121 | $this->getExchangeData($currentDate->format('Y-m-d H:i'), $nextDate->format('Y-m-d H:i')); 122 | $balances = $this->getBalances(); 123 | $usd_balances = array(); 124 | foreach ($balances as $currency => $account_balances) { 125 | foreach ($account_balances as $account => $amount) { 126 | if (!array_key_exists($account, $usd_balances)) { 127 | $usd_balances[$account] = 0; 128 | } 129 | $usd_balances[$account] += $price_history[$currentDate->format('Y-m-d')][$currency] * $amount; 130 | } 131 | } 132 | //print "First included transfer: " . date('c', $this->min_timestamp) . "\n"; 133 | //print "Last included transfer: " . date('c', $this->max_timestamp) . "\n"; 134 | $sbd_report .= $currentDate->format('Y-m-d H:i') . ","; 135 | $steem_report .= $currentDate->format('Y-m-d H:i') . ","; 136 | $usd_report .= $currentDate->format('Y-m-d H:i') . ","; 137 | 138 | foreach($this->exchange_accounts as $exchange_account) { 139 | $sbd_report .= number_format($balances['SBD'][$exchange_account],0,'.','') . ","; 140 | $steem_report .= number_format($balances['STEEM'][$exchange_account],0,'.','') . ","; 141 | $usd_report .= number_format($usd_balances[$exchange_account],0,'.','') . ","; 142 | unset($balances['SBD'][$exchange_account]); 143 | unset($balances['STEEM'][$exchange_account]); 144 | unset($usd_balances[$exchange_account]); 145 | } 146 | $sbd_report .= number_format(0-array_sum($balances['SBD']), 0,'.','') . "\n"; 147 | $steem_report .= number_format(0-array_sum($balances['STEEM']), 0,'.','') . "\n"; 148 | $usd_report .= number_format(0-array_sum($usd_balances), 0,'.','') . "\n"; 149 | 150 | $this->min_timestamp = time(); 151 | $this->max_timestamp = 0; 152 | } 153 | 154 | file_put_contents('STEEM_' . $endDate->format('Y-m-d') . '.txt', $steem_report); 155 | file_put_contents('SBD_' . $endDate->format('Y-m-d') . '.txt', $sbd_report); 156 | file_put_contents('USD_' . $endDate->format('Y-m-d') . '.txt', $usd_report); 157 | 158 | print "## STEEM\n"; 159 | print $steem_report; 160 | print "\n\n\n"; 161 | 162 | print "## SBD\n"; 163 | print $sbd_report; 164 | print "\n\n\n"; 165 | 166 | print "## USD\n"; 167 | print $usd_report; 168 | print "\n\n\n"; 169 | 170 | } 171 | } 172 | 173 | public function runReport($start, $end) 174 | { 175 | $report_width = 70; 176 | $end_display = strtotime($end); 177 | $end_display = date('m/d/Y',strtotime('-1 day', $end_display)); 178 | 179 | print "# Exchange Transfer Activity from " . date('m/d/Y',strtotime($start)) . " to " . $end_display . "\n"; 180 | 181 | $this->getExchangeData($start, $end); 182 | $balances = $this->getBalances(); 183 | 184 | print "First included transfer: " . date('c', $this->min_timestamp) . "\n"; 185 | print "Last included transfer: " . date('c', $this->max_timestamp) . "\n"; 186 | 187 | print "```\n"; 188 | print "\n" . str_pad(" Exchanges ",$report_width,'-',STR_PAD_BOTH) . "\n\n"; 189 | $price_history = $this->SteemAPI->getPriceHistoryInUSD($start, $end); 190 | // Do some reporting for our exchange accounts 191 | $exchanges_sbd = array(); 192 | $exchanges_steem = array(); 193 | $exchanges_total_usd = array(); 194 | foreach($this->exchange_accounts as $exchange_account) { 195 | $exchanges_sbd[$exchange_account] = $balances['SBD'][$exchange_account]; 196 | $exchanges_steem[$exchange_account] = $balances['STEEM'][$exchange_account]; 197 | $exchanges_total_usd[$exchange_account] = ($exchanges_sbd[$exchange_account] * $price_history['average']['SBD']) + ($exchanges_steem[$exchange_account] * $price_history['average']['STEEM']); 198 | unset($balances['SBD'][$exchange_account]); 199 | unset($balances['STEEM'][$exchange_account]); 200 | } 201 | arsort($exchanges_total_usd); 202 | foreach($exchanges_total_usd as $exchange_account => $usd_total) { 203 | print '@' . $exchange_account . ' STEEM transfer total: ' . number_format($exchanges_steem[$exchange_account],3) . "\n"; 204 | print '@' . $exchange_account . ' SBD transfer total: ' . number_format($exchanges_sbd[$exchange_account],3) . "\n"; 205 | } 206 | 207 | print "\n\nPrice Averages: \n"; 208 | print '- STEEM: $' . round($price_history['average']['STEEM'],5) . "\n"; 209 | print '- SBD: $' . round($price_history['average']['SBD'],5) . "\n"; 210 | print "\n\n"; 211 | 212 | //var_dump($price_history); 213 | 214 | // Get USD amounts using our conversion rates for SBD / STEEM 215 | $usd_balances = array(); 216 | foreach ($balances as $currency => $account_balances) { 217 | foreach ($account_balances as $account => $amount) { 218 | if (!array_key_exists($account, $usd_balances)) { 219 | $usd_balances[$account] = 0; 220 | } 221 | $usd_balances[$account] += $price_history['average'][$currency] * $amount; 222 | } 223 | } 224 | asort($usd_balances); 225 | 226 | print "\n" . str_pad(" Total Transferred ",$report_width,'-',STR_PAD_BOTH) . "\n\n"; 227 | print "Total STEEM transferred: " . number_format(array_sum($balances['STEEM']), 3) . "\n"; 228 | print "Total SBD transferred: " . number_format(array_sum($balances['SBD']), 3) . "\n"; 229 | print "Total USD transferred: " . money_format('%(#8n',array_sum($usd_balances)) . "\n"; 230 | 231 | 232 | // Exclude steemit accounts 233 | $steemit_accounts = array('steemit','steemit2','steemit3'); 234 | foreach ($steemit_accounts as $steemit_account) { 235 | if (array_key_exists($steemit_account,$usd_balances)) { 236 | $steem_balances_without_steemit = $balances['STEEM']; 237 | $sbd_balances_without_steemit = $balances['SBD']; 238 | $usd_balances_without_steemit = $usd_balances; 239 | unset($steem_balances_without_steemit[$steemit_account]); 240 | unset($sbd_balances_without_steemit[$steemit_account]); 241 | unset($usd_balances_without_steemit[$steemit_account]); 242 | print "\n" . str_pad(" Total Transferred, Excluding Steemit, inc Account " . $steemit_account,$report_width,'-',STR_PAD_BOTH) . "\n\n"; 243 | print "Total STEEM transferred (excluding " . $steemit_account . "): " . number_format(array_sum($steem_balances_without_steemit), 3) . "\n"; 244 | print "Total SBD transferred (excluding " . $steemit_account . "): " . number_format(array_sum($sbd_balances_without_steemit), 3) . "\n"; 245 | print "Total USD transferred (excluding " . $steemit_account . "): " . money_format('%(#8n',array_sum($usd_balances_without_steemit)) . "\n"; 246 | } 247 | } 248 | 249 | $withdraws = array_filter($usd_balances, function ($v) { 250 | return $v < 0; 251 | }); 252 | $deposits = array_filter($usd_balances, function ($v) { 253 | return $v > 0; 254 | }); 255 | arsort($deposits); 256 | 257 | print "\n" . str_pad(" Withdrawal to Deposit Ratio ",$report_width,'-',STR_PAD_BOTH) . "\n\n"; 258 | 259 | print "Accounts withdrawing: " . count($withdraws) . "\n"; 260 | print "Average withdrawal amount: " . money_format('%(#8n',array_sum($withdraws) / count($withdraws)) . "\n"; 261 | print "Median withdrawal amount: " . money_format('%(#8n',$this->array_median($withdraws)) . "\n"; 262 | 263 | print "\n"; 264 | 265 | print "Accounts depositing: " . count($deposits) . "\n"; 266 | print "Average deposit amount: " . money_format('%(#8n',array_sum($deposits) / count($withdraws)) . "\n"; 267 | print "Median deposit amount: " . money_format('%(#8n',$this->array_median($deposits)) . "\n"; 268 | 269 | print "\n"; 270 | 271 | print "Ratio of withdrawals to deposits: " . number_format(count($withdraws)/count($deposits),2) . "/1\n"; 272 | 273 | print "```\n"; 274 | 275 | 276 | // get top accounts 277 | $top_withdraws = array_slice($withdraws, 0, $this->number_of_top_accounts_to_show); 278 | $top_deposits = array_slice($deposits, 0, $this->number_of_top_accounts_to_show); 279 | 280 | $header = "| | Account| Net Transfer Amount | \n"; 281 | $header .= "|:--:|:----------------:|:------------------------:|\n"; 282 | 283 | 284 | print "\n##
TOP $this->number_of_top_accounts_to_show WITHDRAWALS THIS WEEK
\n\n"; 285 | print $header; 286 | $count = 0; 287 | foreach ($top_withdraws as $account => $amount) { 288 | $count++; 289 | print '|' . $count . '|' . sprintf('%20s',$account) . ': | ' . money_format('%(#8n',$amount) . "|\n"; 290 | } 291 | print "\n##
TOP $this->number_of_top_accounts_to_show DEPOSITS THIS WEEK
\n\n"; 292 | //print "\n## " . str_pad(" TOP $this->number_of_top_accounts_to_show DEPOSITS (powering up?) ",80,'-',STR_PAD_BOTH) . "\n\n"; 293 | print $header; 294 | $count = 0; 295 | foreach ($top_deposits as $account => $amount) { 296 | $count++; 297 | print '|' . $count . '|' . sprintf('%20s',$account) . ': | ' . money_format('%(#8n',$amount) . "|\n"; 298 | } 299 | } 300 | 301 | 302 | public function getBalances() 303 | { 304 | // Calculate transfer balances for SBD and STEEM for each account 305 | $sbd_balances = array(); 306 | $steem_balances = array(); 307 | foreach($this->exchange_accounts as $exchange_account) { 308 | foreach ($this->exchange_data[$exchange_account] as $transfer) { 309 | 310 | if ($transfer['currency'] == 'SBD') { 311 | if (!array_key_exists($transfer['from'], $sbd_balances)) { 312 | $sbd_balances[$transfer['from']] = 0; 313 | } 314 | if (!array_key_exists($transfer['to'], $sbd_balances)) { 315 | $sbd_balances[$transfer['to']] = 0; 316 | } 317 | $sbd_balances[$transfer['to']] += $transfer['amount']; 318 | $sbd_balances[$transfer['from']] -= $transfer['amount']; 319 | } 320 | 321 | if ($transfer['currency'] == 'STEEM') { 322 | if (!array_key_exists($transfer['from'], $steem_balances)) { 323 | $steem_balances[$transfer['from']] = 0; 324 | } 325 | if (!array_key_exists($transfer['to'], $steem_balances)) { 326 | $steem_balances[$transfer['to']] = 0; 327 | } 328 | $steem_balances[$transfer['to']] += $transfer['amount']; 329 | $steem_balances[$transfer['from']] -= $transfer['amount']; 330 | } 331 | 332 | } 333 | } 334 | asort($sbd_balances); 335 | asort($steem_balances); 336 | 337 | //var_dump($steem_balances); 338 | 339 | 340 | return array( 341 | 'SBD' => $sbd_balances, 342 | 'STEEM' => $steem_balances 343 | ); 344 | } 345 | 346 | 347 | public function getResultInfo($blocks) 348 | { 349 | $max_timestamp = 0; 350 | $min_timestamp = 0; 351 | $max_id = 0; 352 | $min_id = 0; 353 | foreach($blocks as $block) { 354 | $timestamp = strtotime($block[1]['timestamp']); 355 | if ($timestamp >= $max_timestamp) { 356 | $max_timestamp = $timestamp; 357 | $max_id = $block[0]; 358 | } 359 | if (!$min_timestamp) { 360 | $min_timestamp = $max_timestamp; 361 | } 362 | if ($timestamp <= $min_timestamp) { 363 | $min_timestamp = $timestamp; 364 | $min_id = $block[0]; 365 | } 366 | } 367 | return array( 368 | 'max_id' => $max_id, 369 | 'max_timestamp' => $max_timestamp, 370 | 'min_id' => $min_id, 371 | 'min_timestamp' => $min_timestamp 372 | ); 373 | } 374 | 375 | public function getAccountTransferHistory($account, $start, $end) 376 | { 377 | $start_timestamp = strtotime($start); 378 | $end_timestamp = strtotime($end); 379 | $limit = 2000; 380 | $params = array($account, -1, $limit); 381 | $result = $this->SteemAPI->getAccountHistory($params); 382 | $info = $this->getResultInfo($result); 383 | $filtered_results = $this->filterAccountHistory($result,$start_timestamp,$end_timestamp,array('transfer','transfer_to_vesting')); 384 | 385 | while ($start_timestamp < $info['min_timestamp']) { 386 | $from = $info['min_id']; 387 | if ($limit > $from) { 388 | $limit = $from; 389 | } 390 | if ($limit == $from && $from == 0) { 391 | break; 392 | } 393 | $params = array($account, $info['min_id'], $limit); 394 | $result = $this->SteemAPI->getAccountHistory($params); 395 | $filtered_results = array_merge( 396 | $filtered_results, 397 | $this->filterAccountHistory($result,$start_timestamp,$end_timestamp,array('transfer','transfer_to_vesting')) 398 | ); 399 | $info = $this->getResultInfo($result); 400 | } 401 | return $filtered_results; 402 | } 403 | 404 | public function filterAccountHistory($result, $start_timestamp, $end_timestamp, $ops = array()) 405 | { 406 | $filtered_results = array(); 407 | if (count($result)) { 408 | foreach($result as $block) { 409 | $timestamp = strtotime($block[1]['timestamp']); 410 | if (in_array($block[1]['op'][0], $ops) 411 | && $timestamp >= $start_timestamp 412 | && $timestamp <= $end_timestamp 413 | ) { 414 | $this->min_timestamp = min($this->min_timestamp,$timestamp); 415 | $this->max_timestamp = max($this->max_timestamp,$timestamp); 416 | $filtered_results[] = $block; 417 | } 418 | } 419 | } 420 | return $filtered_results; 421 | } 422 | 423 | 424 | /** 425 | * Helper Function 426 | **/ 427 | public function array_median($arr) { 428 | $count = count($arr); //total numbers in array 429 | $middleval = floor(($count-1)/2); // find the middle value, or the lowest middle value 430 | if($count % 2) { // odd number, middle is the median 431 | $median = array_slice($arr, $middleval, 1); 432 | $median = array_pop($median); 433 | } else { // even number, calculate avg of 2 medians 434 | $low = array_slice($arr, $middleval, 1); 435 | $low = array_pop($low); 436 | $high = array_slice($arr, $middleval+1, 1); 437 | $high = array_pop($high); 438 | $median = (($low+$high)/2); 439 | } 440 | return $median; 441 | } 442 | 443 | } 444 | -------------------------------------------------------------------------------- /src/SteemTools/Reporting/FollowerStats.php: -------------------------------------------------------------------------------- 1 | SteemAPI = $SteemAPI; 17 | if (array_key_exists('number_of_top_accounts_to_show', $config)) { 18 | $this->number_of_top_accounts_to_show = $config['number_of_top_accounts_to_show']; 19 | } 20 | } 21 | 22 | 23 | public function getAllAccounts() 24 | { 25 | $params = array('*',-1); 26 | $all_accounts = $this->SteemAPI->cachedCall('lookupAccounts', $params, true); 27 | print "getAllAccounts: " . count($all_accounts) . "\n"; 28 | return $all_accounts; 29 | } 30 | 31 | public function getAccountsWithPosts($all_accounts, $start, $batch_size) 32 | { 33 | $some_accounts = array_slice($all_accounts, $start, $batch_size); 34 | $accounts_with_info = $this->SteemAPI->getAccounts($some_accounts); 35 | $active_accounts = $this->filterForActiveAccounts($accounts_with_info); 36 | return $active_accounts; 37 | } 38 | 39 | public function filterForActiveAccounts($accounts) 40 | { 41 | $filtered_accounts = array(); 42 | foreach($accounts as $account) { 43 | if ($account['post_count'] > 0) { 44 | $filtered_accounts[] = $account['name']; 45 | } 46 | } 47 | return $filtered_accounts; 48 | } 49 | 50 | public function getTotalActiveAccounts() 51 | { 52 | $file = "getAllAccountsWithPosts_data.txt"; 53 | $lines = count(file($file)); 54 | return $lines; 55 | } 56 | 57 | public function saveAllActiveAccountsWithPostsToAFile() 58 | { 59 | print "saveAllActiveAccountsWithPostsToAFile...\n"; 60 | $all_accounts = $this->getAllAccounts(); 61 | $total = count($all_accounts); 62 | $start = $this->getStartCountFromFile('getAllAccountsWithPosts'); 63 | $batch_size = 100; 64 | while($total > $batch_size) { 65 | $this->saveStartCountToFile('getAllAccountsWithPosts', $start); 66 | $filtered_accounts = $this->getAccountsWithPosts($all_accounts, $start, $batch_size); 67 | $start += $batch_size; 68 | $total -= $batch_size; 69 | print '.'; 70 | foreach ($filtered_accounts as $filtered_account) { 71 | file_put_contents('getAllAccountsWithPosts_data.txt', $filtered_account . "\n", FILE_APPEND); 72 | } 73 | } 74 | $start -= $batch_size; 75 | $filtered_accounts = $this->getAccountsWithPosts($all_accounts, $start, $total); 76 | print '.'; 77 | foreach ($filtered_accounts as $filtered_account) { 78 | file_put_contents('getAllAccountsWithPosts_data.txt', $filtered_account . "\n", FILE_APPEND); 79 | } 80 | print "\n"; 81 | print "Active Accounts (at least one post): " . $this->getTotalActiveAccounts() . "\n"; 82 | } 83 | 84 | public function saveFollowerCounts() { 85 | print "saveFollowerCounts...\n"; 86 | $min_threshold = 10; 87 | $follower_counts = array(); 88 | $active_accounts = @file('getAllAccountsWithPosts_data.txt'); 89 | if (!$active_accounts) { 90 | $this->saveStartCountToFile('getAllAccountsWithPosts', 0); 91 | $this->saveAllActiveAccountsWithPostsToAFile(); 92 | $active_accounts = file('getAllAccountsWithPosts_data.txt'); 93 | } 94 | $start = $this->getStartCountFromFile('saveFollowerCounts'); 95 | if (!$start) { 96 | $start = 0; 97 | $this->saveStartCountToFile('saveFollowerCounts', $start); 98 | } 99 | print "saveFollowerCounts: Starting at $start\n"; 100 | for ($i = $start; $iSteemAPI->getFollowerCount($account); 107 | $this->saveStartCountToFile('saveFollowerCounts', $i); 108 | if ($follower_count >= $min_threshold) { 109 | file_put_contents('saveFollowerCounts_data.txt', $account . ',' . $follower_count . "\n", FILE_APPEND); 110 | } 111 | } 112 | } 113 | 114 | public function compareTwoReports($file1, $file2) { 115 | $file1 = @fopen($file1,"r"); 116 | $file2 = @fopen($file2,"r"); 117 | if (!$file1 || !$file2) { 118 | die("Files not found."); 119 | } 120 | 121 | $accounts1 = array(); 122 | while(!feof($file1)) { 123 | $line = fgetcsv($file1); 124 | $accounts1[trim($line[0])] = trim($line[1]); 125 | } 126 | fclose($file1); 127 | foreach ($this->exploiters as $account) { 128 | if (isset($accounts1[$account])) { 129 | unset($accounts1[$account]); 130 | } 131 | } 132 | arsort($accounts1); 133 | 134 | $accounts2 = array(); 135 | while(!feof($file2)) { 136 | $line = fgetcsv($file2); 137 | $accounts2[trim($line[0])] = trim($line[1]); 138 | } 139 | fclose($file2); 140 | foreach ($this->exploiters as $account) { 141 | if (isset($accounts2[$account])) { 142 | unset($accounts2[$account]); 143 | } 144 | } 145 | arsort($accounts2); 146 | $ranked2 = array(); 147 | $count = 0; 148 | foreach ($accounts2 as $account => $follower_count) { 149 | $count++; 150 | $ranked2[$account] = array( 151 | 'rank' => $count, 152 | 'follower_count' => $follower_count 153 | ); 154 | } 155 | 156 | $output = array(); 157 | 158 | $count = 0; 159 | foreach ($accounts1 as $account => $follower_count) { 160 | $count++; 161 | if ($count > $this->number_of_top_accounts_to_show) { 162 | break; 163 | } 164 | $previous_rank = isset($ranked2[$account]) ? $ranked2[$account]['rank'] : '0'; 165 | $previous_follower_count = isset($ranked2[$account]) ? $ranked2[$account]['follower_count'] : '0'; 166 | $change_in_rank = ($previous_rank) ? ($previous_rank - $count) : '**NEW**'; 167 | $change_in_follower_count = ($previous_follower_count) ? ($follower_count - $previous_follower_count) : '**NEW**'; 168 | $change_in_follower_count_percentage = ''; 169 | if ($previous_follower_count) { 170 | $change_in_follower_count_percentage = number_format(($change_in_follower_count / $follower_count) * 100, 0); 171 | } 172 | $output[$account] = array( 173 | 'rank' => $count, 174 | 'follower_count' => $follower_count, 175 | 'change_in_rank' => $change_in_rank, 176 | 'change_in_follower_count' => $change_in_follower_count, 177 | 'change_in_follower_count_percentage' => $change_in_follower_count_percentage, 178 | ); 179 | 180 | //var_dump($output[$account]); 181 | 182 | } 183 | 184 | 185 | //$rising_stars = $output; 186 | //$rising_stars = $this->sortBy('change_in_rank', $output, 'desc'); 187 | //var_dump($output); 188 | //die(); 189 | /* 190 | 191 | | 46 |+1627 | @0 |464 |+451 |97%| 192 | | 50 |+82 | @1 |458 |+305 |67%| 193 | | 98 |+63 | @2 |264 |+131 |50%| 194 | | 15 |+42 | @3 |641 |+332 |52%| 195 | | 77 |+38 | @4 |314 |+142 |45%| 196 | | 96 |+38 | @5 |267 |+114 |43%| 197 | | 72 |+27 | @6 |345 |+158 |46%| 198 | | 61 |+18 | @7 |410 |+177 |43%| 199 | | 22 |+15 | @8 |596 |+184 |31%| 200 | | 28 |+14 | @9 |572 |+189 |33%| 201 | | 11 |+12 | @10 |723 |+246 |34%| 202 | | 9 |+11 | @11 |822 |+337 |41%| 203 | | 80 |+10 | @12 |307 |+101 |33%| 204 | | 82 |+9 | @13 |305 |+100 |33%| 205 | | 18 |+9 | @14 |628 |+162 |26%| 206 | 207 | | 44 | | @31 |485 | || 208 | | 68 | | @32 |380 | || 209 | | 89 | | @38 |292 | || 210 | | 100 | | @40 |263 | || 211 | | 56 | | @42 |437 | || 212 | 213 | | 67 | | @35 |380 | || 214 | | 88 | | @37 |292 | || 215 | | 55 | | @42 |437 | || 216 | 217 | 218 | */ 219 | 220 | 221 | 222 | $header = "| Rank | Change | Account| Number of Followers | Change | | \n"; 223 | $header .= "|:--:|:--:|:----------------:|:------------------------:|:--:|:--:|\n"; 224 | 225 | print "\n##
TOP $this->number_of_top_accounts_to_show USERS BY FOLLOWER COUNT
\n\n"; 226 | print $header; 227 | foreach ($output as $account => $details) { 228 | if ($details['rank'] > $this->number_of_top_accounts_to_show) { 229 | break; 230 | } 231 | print '| ' . $details['rank']; 232 | print ' |'; 233 | if ($details['change_in_rank'] > 0) { 234 | print '+' . $details['change_in_rank']; 235 | } elseif ($details['change_in_rank'] < 0) { 236 | print $details['change_in_rank']; 237 | } 238 | print ' |' . sprintf('%15s','@'.$account); 239 | print ' |' . $details['follower_count']; 240 | print ' |'; 241 | if ($details['change_in_follower_count'] > 0) { 242 | print '+' . $details['change_in_follower_count']; 243 | } elseif ($details['change_in_follower_count'] < 0) { 244 | print $details['change_in_follower_count']; 245 | } 246 | print ' |'; 247 | if ($details['change_in_follower_count_percentage'] != 0) { 248 | print $details['change_in_follower_count_percentage'] . '%'; 249 | } 250 | print "|\n"; 251 | } 252 | 253 | print "# Stats\n"; 254 | 255 | print "| | Count | Change | % Change |\n"; 256 | print "|:-----:|:-----:|:-----:|:----------------:|\n"; 257 | print "| Total number of accounts: | 64,106 | +8,290 | +14.8%|\n"; 258 | print "|Accounts with at least one post: | 21,311 | +1,916 | +9.8%\n"; 259 | print "|Accounts with at least **ten** followers: | 1,690 | N/A | |\n"; 260 | 261 | } 262 | 263 | public function printTopFollowed() { 264 | print "printTopFollowed...\n"; 265 | $accounts = array(); 266 | $file = @fopen("saveFollowerCounts_data.txt","r"); 267 | if (!$file) { 268 | $this->saveStartCountToFile('saveFollowerCounts', 0); 269 | $this->saveFollowerCounts(); 270 | $file = @fopen("saveFollowerCounts_data.txt","r"); 271 | } 272 | $count = 0; 273 | while(!feof($file)) { 274 | $count++; 275 | $line = fgetcsv($file); 276 | $accounts[trim($line[0])] = trim($line[1]); 277 | } 278 | fclose($file); 279 | 280 | $total = $this->getStartCountFromFile('saveFollowerCounts'); 281 | if ($count < $total) { 282 | $this->saveFollowerCounts(); 283 | $file = @fopen("saveFollowerCounts_data.txt","r"); 284 | while(!feof($file)) { 285 | $line = fgetcsv($file); 286 | $accounts[trim($line[0])] = trim($line[1]); 287 | } 288 | } 289 | arsort($accounts); 290 | 291 | $header = "| | Account| Number of Followers | \n"; 292 | $header .= "|:--:|:----------------:|:------------------------:|\n"; 293 | 294 | print "\n##
TOP $this->number_of_top_accounts_to_show USERS BY FOLLOWER COUNT
\n\n"; 295 | print $header; 296 | $count = 0; 297 | foreach ($accounts as $account => $follower_count) { 298 | $count++; 299 | if ($count > $this->number_of_top_accounts_to_show) { 300 | break; 301 | } 302 | print '| ' . $count . ' |' . sprintf('%15s','@'.$account) . ': | ' . $follower_count . " |\n"; 303 | } 304 | } 305 | 306 | public function getStartCountFromFile($method) 307 | { 308 | $start = @file_get_contents($method . '_start.txt'); 309 | if (!$start) { 310 | $start = 0; 311 | $this->saveStartCountToFile($method, $start); 312 | } 313 | return $start; 314 | } 315 | 316 | public function saveStartCountToFile($method, $start) 317 | { 318 | file_put_contents($method . '_start.txt',$start); 319 | } 320 | 321 | public function sortBy($field, &$array, $direction = 'asc') 322 | { 323 | usort($array, create_function('$a, $b', ' 324 | $a = $a["' . $field . '"]; 325 | $b = $b["' . $field . '"]; 326 | 327 | if ($a == $b) 328 | { 329 | return 0; 330 | } 331 | 332 | return ($a ' . ($direction == 'desc' ? '>' : '<') .' $b) ? -1 : 1; 333 | ')); 334 | 335 | return true; 336 | } 337 | 338 | 339 | } 340 | -------------------------------------------------------------------------------- /src/SteemTools/SteemAPI.php: -------------------------------------------------------------------------------- 1 | SteemServiceLayer = $SteemServiceLayer; 12 | } 13 | 14 | public function getPriceHistoryInUSD($start, $end) 15 | { 16 | $btc_prices_in_usd = json_decode(file_get_contents('https://api.coindesk.com/v1/bpi/historical/close.json'), true); 17 | 18 | $start_timestamp = strtotime($start); 19 | $end_timestamp = strtotime($end); 20 | $sbd_prices = json_decode(file_get_contents('https://min-api.cryptocompare.com/data/histoday?fsym=SBD*&tsym=BTC&limit=7&aggregate=1&toTs=' . $end_timestamp), true); 21 | $steem_prices = json_decode(file_get_contents('https://min-api.cryptocompare.com/data/histoday?fsym=STEEM&tsym=BTC&limit=7&aggregate=1&toTs=' . $end_timestamp), true); 22 | 23 | $price_history = array(); 24 | $startDate = new \DateTime($start); 25 | $endDate = new \DateTime($end); 26 | $days = $startDate->diff($endDate)->format("%a"); 27 | for ($day = 0; $day < $days; $day++) { 28 | $currentDate = clone $startDate; 29 | $nextDate = clone $startDate; 30 | $currentDate->add(new \DateInterval('P' . $day . 'D')); 31 | $nextDate->add(new \DateInterval('P' . ($day+1) . 'D')); 32 | $btc_price = $btc_prices_in_usd['bpi'][$currentDate->format("Y-m-d")]; 33 | $timestamp = strtotime($currentDate->format("Y-m-d")); 34 | foreach ($sbd_prices['Data'] as $line) { 35 | if ($line['time'] == $timestamp) { 36 | $sbd_price = $line['close'] * $btc_price; 37 | } 38 | } 39 | foreach ($steem_prices['Data'] as $line) { 40 | if ($line['time'] == $timestamp) { 41 | $steem_price = $line['close'] * $btc_price; 42 | } 43 | } 44 | $price_history[$currentDate->format("Y-m-d")] = array( 45 | 'BTC' => $btc_price, 46 | 'SBD' => $sbd_price, 47 | 'STEEM' => $steem_price 48 | ); 49 | } 50 | 51 | /* 52 | $end_timestamp = strtotime($end); 53 | $sbd_prices = json_decode(file_get_contents('https://poloniex.com/public?command=returnChartData¤cyPair=BTC_SBD&start=' . $start_timestamp . '&end=' . $end_timestamp . '&period=86400'), true); 54 | $steem_prices = json_decode(file_get_contents('https://poloniex.com/public?command=returnChartData¤cyPair=BTC_STEEM&start=' . $start_timestamp . '&end=' . $end_timestamp . '&period=86400'), true); 55 | 56 | $price_history = array(); 57 | 58 | $startDate = new \DateTime($start); 59 | $endDate = new \DateTime($end); 60 | $days = $startDate->diff($endDate)->format("%a"); 61 | for ($day = 0; $day < $days; $day++) { 62 | $currentDate = clone $startDate; 63 | $nextDate = clone $startDate; 64 | $currentDate->add(new \DateInterval('P' . $day . 'D')); 65 | $nextDate->add(new \DateInterval('P' . ($day+1) . 'D')); 66 | 67 | $btc_price = $btc_prices_in_usd['bpi'][$currentDate->format("Y-m-d")]; 68 | $sbd_price = $sbd_prices[$day]['weightedAverage'] * $btc_price; 69 | $steem_price = $steem_prices[$day]['weightedAverage'] * $btc_price; 70 | 71 | $price_history[$currentDate->format("Y-m-d")] = array( 72 | 'BTC' => $btc_price, 73 | 'SBD' => $sbd_price, 74 | 'STEEM' => $steem_price 75 | ); 76 | } 77 | */ 78 | 79 | $price_total_sbd = 0; 80 | $price_total_steem = 0; 81 | foreach($price_history as $price) { 82 | $price_total_sbd += $price['SBD']; 83 | $price_total_steem += $price['STEEM']; 84 | } 85 | $price_history['average'] = array( 86 | 'SBD' => $price_total_sbd / $days, 87 | 'STEEM' => $price_total_steem / $days 88 | ); 89 | 90 | //var_dump($price_history); 91 | 92 | return $price_history; 93 | } 94 | 95 | public function getContent($params) 96 | { 97 | $result = $this->SteemServiceLayer->call('get_content', $params); 98 | return $result; 99 | } 100 | 101 | public function getAccountVotes($params) 102 | { 103 | $result = $this->SteemServiceLayer->call('get_account_votes', $params); 104 | return $result; 105 | } 106 | 107 | public function getAccountHistory($params) 108 | { 109 | $result = $this->SteemServiceLayer->call('get_account_history', $params); 110 | return $result; 111 | } 112 | 113 | public function getFollowerCount($account) 114 | { 115 | $followers = $this->getFollowers($account); 116 | return count($followers); 117 | } 118 | 119 | public function getFollowers($account, $start = '') 120 | { 121 | $limit = 100; 122 | $followers = array(); 123 | $params = array($this->getFollowAPIID(),'get_followers',array($account,$start,'blog',$limit)); 124 | $followers = $this->SteemServiceLayer->call('call', $params); 125 | if (count($followers) == $limit) { 126 | $last_account = $followers[$limit-1]; 127 | $more_followers = $this->getFollowers($account, $last_account['follower']); 128 | array_pop($followers); 129 | $followers = array_merge($followers, $more_followers); 130 | } 131 | return $followers; 132 | } 133 | 134 | public function lookupAccounts($params) 135 | { 136 | $accounts = $this->SteemServiceLayer->call('lookup_accounts', $params); 137 | return $accounts; 138 | } 139 | 140 | public function getAccounts($accounts) 141 | { 142 | $get_accounts_results = $this->SteemServiceLayer->call('get_accounts', array($accounts)); 143 | return $get_accounts_results; 144 | } 145 | 146 | public function getFollowAPIID() 147 | { 148 | return $this->getAPIID('follow_api'); 149 | } 150 | 151 | public function getAPIID($api_name) 152 | { 153 | if (array_key_exists($api_name, $this->api_ids)) { 154 | return $this->api_ids[$api_name]; 155 | } 156 | $response = $this->SteemServiceLayer->call('call', array(1,'get_api_by_name',array($api_name))); 157 | $this->api_ids[$api_name] = $response; 158 | return $response; 159 | } 160 | 161 | public function cachedCall($call, $params, $serialize = false, $batch = false, $batch_size = 100) 162 | { 163 | $filename = 'cache/' . $call . md5(serialize($params)) . '_data.txt'; 164 | $data = @file_get_contents($filename); 165 | if ($data) { 166 | if ($serialize) { 167 | $data = unserialize($data); 168 | } 169 | return $data; 170 | } 171 | $data = $this->{$call}($params); 172 | 173 | if ($serialize) { 174 | $data_for_file = serialize($data); 175 | } else { 176 | $data_for_file = $data; 177 | } 178 | file_put_contents($filename,$data_for_file); 179 | return $data; 180 | /* 181 | $start = @file_get_contents($call . '_start.txt'); 182 | if (!$start) { 183 | $start = 0; 184 | file_put_contents($call . '_start.txt',$start); 185 | } 186 | */ 187 | } 188 | 189 | 190 | // functions for filtering through account history 191 | public function getResultInfo($blocks) 192 | { 193 | $max_timestamp = 0; 194 | $min_timestamp = 0; 195 | $max_id = 0; 196 | $min_id = 0; 197 | foreach($blocks as $block) { 198 | $timestamp = strtotime($block[1]['timestamp']); 199 | if ($timestamp >= $max_timestamp) { 200 | $max_timestamp = $timestamp; 201 | $max_id = $block[0]; 202 | } 203 | if (!$min_timestamp) { 204 | $min_timestamp = $max_timestamp; 205 | } 206 | if ($timestamp <= $min_timestamp) { 207 | $min_timestamp = $timestamp; 208 | $min_id = $block[0]; 209 | } 210 | } 211 | return array( 212 | 'max_id' => $max_id, 213 | 'max_timestamp' => $max_timestamp, 214 | 'min_id' => $min_id, 215 | 'min_timestamp' => $min_timestamp 216 | ); 217 | } 218 | 219 | public function getAccountHistoryFiltered($account, $types, $start, $end) 220 | { 221 | $start_timestamp = strtotime($start); 222 | $end_timestamp = strtotime($end); 223 | $limit = 2000; 224 | $params = array($account, -1, $limit); 225 | $result = $this->getAccountHistory($params); 226 | $info = $this->getResultInfo($result); 227 | $filtered_results = $this->filterAccountHistory($result,$start_timestamp,$end_timestamp,$types); 228 | 229 | while ($start_timestamp < $info['min_timestamp']) { 230 | $from = $info['min_id']; 231 | if ($limit > $from) { 232 | $limit = $from; 233 | } 234 | $params = array($account, $info['min_id'], $limit); 235 | $result = $this->getAccountHistory($params); 236 | $filtered_results = array_merge( 237 | $filtered_results, 238 | $this->filterAccountHistory($result,$start_timestamp,$end_timestamp,$types) 239 | ); 240 | $info = $this->getResultInfo($result); 241 | } 242 | 243 | return $filtered_results; 244 | } 245 | 246 | public function filterAccountHistory($result, $start_timestamp, $end_timestamp, $ops) 247 | { 248 | $filtered_results = array(); 249 | if (count($result)) { 250 | foreach($result as $block) { 251 | $timestamp = strtotime($block[1]['timestamp']); 252 | if (in_array($block[1]['op'][0], $ops) 253 | && $timestamp >= $start_timestamp 254 | && $timestamp <= $end_timestamp 255 | ) { 256 | $filtered_results[] = $block; 257 | } 258 | } 259 | } 260 | return $filtered_results; 261 | } 262 | 263 | public function getOpData($result) 264 | { 265 | $data = array(); 266 | foreach($result as $block) { 267 | $data[] = $block[1]['op'][1]; 268 | } 269 | return $data; 270 | } 271 | 272 | private $dynamic_global_properties = array(); 273 | public function getProps($refresh = false) 274 | { 275 | if ($refresh || count($this->dynamic_global_properties) == 0) { 276 | $this->dynamic_global_properties = $this->SteemServiceLayer->call('get_dynamic_global_properties', array()); 277 | } 278 | return $this->dynamic_global_properties; 279 | } 280 | 281 | public function getConversionRate() { 282 | $props = $this->getProps(); 283 | $values = array( 284 | 'total_vests' => (float) $props['total_vesting_shares'], 285 | 'total_vest_steem' => (float) $props['total_vesting_fund_steem'], 286 | ); 287 | return $values; 288 | } 289 | 290 | public function vest2sp($value) 291 | { 292 | $values = $this->getConversionRate(); 293 | return round($values['total_vest_steem'] * ($value / $values['total_vests']), 3); 294 | } 295 | 296 | public function getAuthorAndPermLink($authorperm) 297 | { 298 | list($author,$permlink,$original) = explode('/',$authorperm, 3); 299 | return array('author' => $author, 'permlink' => $permlink); 300 | } 301 | 302 | } -------------------------------------------------------------------------------- /src/SteemTools/SteemServiceLayer.php: -------------------------------------------------------------------------------- 1 | debug = $config['debug']; 22 | } 23 | if (array_key_exists('webservice_url', $config)) { 24 | $this->webservice_url = $config['webservice_url']; 25 | } 26 | if (array_key_exists('throw_exception', $config)) { 27 | $this->throw_exception = $config['throw_exception']; 28 | } 29 | } 30 | 31 | public function call($method, $params = array()) { 32 | $request = $this->getRequest($method, $params); 33 | $response = $this->curl($request); 34 | if (is_null($response) || array_key_exists('error', $response)) { 35 | if ($this->throw_exception) { 36 | if (is_null($response)) { 37 | throw new Exception($method); 38 | } else { 39 | throw new Exception($response['error']); 40 | } 41 | } else { 42 | if (is_null($response)) { 43 | print "We got no response...\n"; 44 | var_dump($method); 45 | var_dump($params); 46 | } else { 47 | print "We got an error response...\n"; 48 | var_dump($method); 49 | var_dump($params); 50 | var_dump($response['error']); 51 | } 52 | die(); 53 | } 54 | } 55 | return $response['result']; 56 | } 57 | 58 | public function getRequest($method, $params) { 59 | $request = array( 60 | "jsonrpc" => "2.0", 61 | "method" => $method, 62 | "params" => $params, 63 | "id" => 1 64 | ); 65 | $request_json = json_encode($request); 66 | 67 | if ($this->debug) { 68 | print $request_json . "\n"; 69 | } 70 | 71 | return $request_json; 72 | } 73 | 74 | public function curl($data) { 75 | $ch = curl_init(); 76 | curl_setopt($ch, CURLOPT_URL, $this->webservice_url); 77 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); 78 | curl_setopt($ch, CURLOPT_POSTFIELDS, $data); 79 | $result = curl_exec($ch); 80 | 81 | if ($this->debug) { 82 | print $result . "\n"; 83 | } 84 | 85 | $result = json_decode($result, true); 86 | 87 | return $result; 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /steem_rate.php: -------------------------------------------------------------------------------- 1 | 1) ? 's' : ''; 15 | print "Sleeping $sleep_time_in_minutes minute$s...\n"; 16 | for($i=0; $i<$sleep_time_in_minutes; $i++) { 17 | print "."; 18 | sleep(60); 19 | } 20 | print "\n"; 21 | 22 | $end_exchange_rate = getExchangeRate($debug); 23 | $vests = getVESTS($user_name, $debug); 24 | $ending_balance = convertToSteemPower($vests, $end_exchange_rate); 25 | 26 | if ($debug) { 27 | print " " . time() . ": Ending Steem Power balance: " . $ending_balance . "\n"; 28 | } 29 | 30 | $change = $ending_balance - $starting_balance; 31 | $steem_power_per_hour = ($change / $sleep_time_in_minutes) * 60; 32 | 33 | $interest_rate_per_hour = number_format($steem_power_per_hour / $ending_balance * 100, 4); 34 | 35 | print "--------------------------------\n"; 36 | print "Interest Rate Per Hour: $interest_rate_per_hour%\n"; 37 | print "Steem Power Per Hour: $steem_power_per_hour\n"; 38 | print "Interest Rate Per Week: " . ($interest_rate_per_hour * 24 * 7) . "%\n"; 39 | print "Steem Power Per Week: " . ($steem_power_per_hour * 24 * 7) . "\n"; 40 | -------------------------------------------------------------------------------- /tests/Bots/CopyCatVoterTest.php: -------------------------------------------------------------------------------- 1 | false, 9 | 'looptime_in_seconds' => 10, 10 | //'reminder_in_seconds' => 3, 11 | 'auto_vote' => true, 12 | 'delay_in_minutes_to_vote_post_by_followed_author' => 20, 13 | ); 14 | 15 | $CopyCatVoter = new \SteemTools\Bots\CopyCatVoter($api, $config); 16 | 17 | $voter_account = 'lukestokes'; 18 | $account_to_copy = 'robinhoodwhale'; 19 | 20 | $CopyCatVoter->run($voter_account,$account_to_copy); 21 | -------------------------------------------------------------------------------- /tests/Bots/CopyCatVoterTestCurationReport.php: -------------------------------------------------------------------------------- 1 | currationReport('lukestokes'); 9 | -------------------------------------------------------------------------------- /tests/Reporting/ExchangeTransfersTest.php: -------------------------------------------------------------------------------- 1 | runReport($start, $end); 13 | //$ExchangeTransfers->runHourlyReport($start, $end); 14 | 15 | print "End time: " . date('c') . "\n"; 16 | -------------------------------------------------------------------------------- /tests/Reporting/FollowerStatsTest.php: -------------------------------------------------------------------------------- 1 | true); 5 | $params = array(); 6 | 7 | $SteemServiceLayer = new \SteemTools\SteemServiceLayer($params); 8 | $api = new \SteemTools\SteemAPI($SteemServiceLayer); 9 | //$FollowerStats = new \SteemTools\Reporting\FollowerStats($api); 10 | 11 | //$result = $FollowerStats->getAllAccounts(); 12 | //var_dump(count($result)); 13 | 14 | //$FollowerStats->printTopFollowed(); 15 | 16 | //$FollowerStats->compareTwoReports('saveFollowerCounts_data.txt','saveFollowerCounts_data_2016-08-18.txt'); 17 | 18 | 19 | 20 | $witness_account = "lukestokes.mhth"; 21 | $controller_account = "lukestokes"; 22 | $followers = $api->cachedCall('getFollowers', $controller_account, true); 23 | $accounts = array(); 24 | foreach($followers as $follower) { 25 | $accounts[] = $follower['follower']; 26 | } 27 | $account_records = $api->cachedCall('getAccounts', $accounts, true); 28 | $accounts_by_vest = array(); 29 | $accounts = array(); 30 | foreach($account_records as $account) { 31 | $accounts[$account['name']] = $account; 32 | $accounts_by_vest[$account['name']] = (int) str_replace(' VESTS', '', $account['vesting_shares']); 33 | } 34 | 35 | arsort($accounts_by_vest); 36 | $top_accounts = array_slice($accounts_by_vest,0,30); 37 | 38 | function showPossibilities($top_accounts,$accounts,$witness_account,$show_available_only = false) 39 | { 40 | $available_vests = 0; 41 | foreach($top_accounts as $account_name => $vests) { 42 | $print_account = true; 43 | $witnesses_voted_for = $accounts[$account_name]['witnesses_voted_for']; 44 | $proxy = $accounts[$account_name]['proxy']; 45 | $witness_votes = $accounts[$account_name]['witness_votes']; 46 | $voted = " Already Voted? "; 47 | $include_vests = true; 48 | if (in_array($witness_account, $witness_votes)) { 49 | $voted .= "YES"; 50 | $print_account = !$show_available_only; 51 | $include_vests = false; 52 | } else { 53 | $voted .= "NO"; 54 | } 55 | $free_votes = (30 - $witnesses_voted_for); 56 | if ($print_account && $show_available_only) { 57 | $print_account = ($free_votes > 0); 58 | } 59 | if ($print_account && $show_available_only) { 60 | $print_account = !($proxy != ''); 61 | } 62 | $pad_amount = 30; 63 | if ($print_account) { 64 | if ($include_vests) { 65 | $available_vests += $accounts[$account_name]['vesting_shares']; 66 | } 67 | print str_pad($account_name,$pad_amount,' ',STR_PAD_RIGHT); 68 | print str_pad('Proxy: ' . $proxy,$pad_amount,' ',STR_PAD_RIGHT); 69 | print str_pad($voted,$pad_amount,' ',STR_PAD_RIGHT); 70 | print str_pad($vests . " VESTS",$pad_amount,' ',STR_PAD_LEFT); 71 | print str_pad($free_votes . " votes free",$pad_amount,' ',STR_PAD_LEFT); 72 | print "\n"; 73 | } 74 | } 75 | print "Total Available MVESTS: " . round($available_vests/1000000) . "\n"; 76 | print "\n"; 77 | } 78 | 79 | showPossibilities($top_accounts,$accounts,$witness_account); 80 | showPossibilities($top_accounts,$accounts,$witness_account,true); 81 | 82 | //var_dump($top_accounts); 83 | 84 | /* 85 | $top_accounts = array_keys($accounts_by_vest); 86 | for ($i = 0; $i < 20; $i++) { 87 | print $top_accounts[$i] . "\n"; 88 | } 89 | */ 90 | 91 | //var_dump(count($accounts_with_info)); 92 | //var_dump(array_pop($accounts_with_info)); 93 | 94 | 95 | //$accounts_with_info = $api->getAccounts(array('tombstone')); 96 | //var_dump($accounts_with_info); 97 | 98 | 99 | //vesting_shares 100 | //proxy 101 | //witnesses_voted_for 102 | // witness_votes -------------------------------------------------------------------------------- /tests/Reporting/FollowerWitnessVotes.php: -------------------------------------------------------------------------------- 1 | true); 5 | $params = array(); 6 | 7 | $SteemServiceLayer = new \SteemTools\SteemServiceLayer($params); 8 | $api = new \SteemTools\SteemAPI($SteemServiceLayer); 9 | 10 | $witness_account = "lukestokes.mhth"; 11 | $controller_account = "lukestokes"; 12 | 13 | $followers = $api->cachedCall('getFollowers', $controller_account, true); 14 | $accounts = array(); 15 | foreach($followers as $follower) { 16 | $accounts[] = $follower['follower']; 17 | } 18 | $account_records = $api->cachedCall('getAccounts', $accounts, true); 19 | $accounts_by_vest = array(); 20 | $accounts = array(); 21 | foreach($account_records as $account) { 22 | $accounts[$account['name']] = $account; 23 | $accounts_by_vest[$account['name']] = (int) str_replace(' VESTS', '', $account['vesting_shares']); 24 | } 25 | 26 | arsort($accounts_by_vest); 27 | $top_accounts = array_slice($accounts_by_vest,0,30); 28 | 29 | function showPossibilities($top_accounts,$accounts,$witness_account,$show_available_only = false) 30 | { 31 | $available_vests = 0; 32 | foreach($top_accounts as $account_name => $vests) { 33 | $print_account = true; 34 | $witnesses_voted_for = $accounts[$account_name]['witnesses_voted_for']; 35 | $proxy = $accounts[$account_name]['proxy']; 36 | $witness_votes = $accounts[$account_name]['witness_votes']; 37 | $voted = " Already Voted? "; 38 | $include_vests = true; 39 | if (in_array($witness_account, $witness_votes)) { 40 | $voted .= "YES"; 41 | $print_account = !$show_available_only; 42 | $include_vests = false; 43 | } else { 44 | $voted .= "NO"; 45 | } 46 | $free_votes = (30 - $witnesses_voted_for); 47 | if ($print_account && $show_available_only) { 48 | $print_account = ($free_votes > 0); 49 | } 50 | if ($print_account && $show_available_only) { 51 | $print_account = !($proxy != ''); 52 | } 53 | $pad_amount = 20; 54 | if ($print_account) { 55 | if ($include_vests) { 56 | $available_vests += $accounts[$account_name]['vesting_shares']; 57 | } 58 | print str_pad($account_name,$pad_amount,' ',STR_PAD_RIGHT); 59 | print str_pad('Proxy: ' . $proxy,$pad_amount,' ',STR_PAD_RIGHT); 60 | print str_pad($voted,$pad_amount,' ',STR_PAD_RIGHT); 61 | print str_pad($vests . " VESTS",$pad_amount,' ',STR_PAD_LEFT); 62 | print str_pad($free_votes . " votes free",$pad_amount,' ',STR_PAD_LEFT); 63 | print "\n"; 64 | } 65 | } 66 | print "Total Available MVESTS: " . round($available_vests/1000000) . "\n"; 67 | print "\n"; 68 | } 69 | 70 | showPossibilities($top_accounts,$accounts,$witness_account); 71 | showPossibilities($top_accounts,$accounts,$witness_account,true); 72 | -------------------------------------------------------------------------------- /tests/Reporting/comments.php: -------------------------------------------------------------------------------- 1 | false)); 9 | $api = new \SteemTools\SteemAPI($SteemServiceLayer); 10 | $comments = $api->getAccountHistoryFiltered('jedau', array('comment'), '2016-08-01', '2016-08-30'); 11 | $comments = $api->getOpData($comments); 12 | foreach($comments as $comment) { 13 | if ($comment['parent_author'] == 'lukestokes') { 14 | var_dump($comment); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/Reporting/promotions.php: -------------------------------------------------------------------------------- 1 | getAccountHistoryFiltered('null',array('transfer'),$start,$end); 16 | $transfers = $api->getOpData($transfers); 17 | 18 | $promotions = array(); 19 | 20 | foreach ($transfers as $transfer) { 21 | if (!array_key_exists($transfer['memo'], $promotions)) { 22 | $promotions[$transfer['memo']] = array( 23 | 'bid_count' => 0, 24 | 'bid_total' => 0.0, 25 | 'permlink' => $transfer['memo'], 26 | 'bidders' => array() 27 | ); 28 | } 29 | if (strpos($transfer['amount'], 'SBD') !== false) { 30 | $amount = str_replace(' SBD', '', $transfer['amount']); 31 | $promotions[$transfer['memo']]['bid_count'] += 1; 32 | $promotions[$transfer['memo']]['bid_total'] += $amount; 33 | if (!array_key_exists($transfer['from'], $promotions[$transfer['memo']]['bidders'])) { 34 | $promotions[$transfer['memo']]['bidders'][$transfer['from']] = 0.0; 35 | } 36 | $promotions[$transfer['memo']]['bidders'][$transfer['from']] += $amount; 37 | } 38 | 39 | } 40 | 41 | /* Code for getting content details of individual posts, such as the last payout. */ 42 | /* 43 | foreach ($promotions as $permlink => $promotion) { 44 | $author_and_link = $api->getAuthorAndPermLink($permlink); 45 | $author_and_link['author'] = str_replace("@", "", $author_and_link['author']); 46 | $content = $api->getContent( 47 | array( 48 | $author_and_link['author'], 49 | $author_and_link['permlink'] 50 | ) 51 | ); 52 | if ($content['last_payout'] != '1970-01-01T00:00:00') { 53 | print $content['last_payout'] . "," . $author_and_link['author'] . "," . $author_and_link['permlink'] . "," . $promotion['bid_total'] . "\n"; 54 | } 55 | } 56 | */ 57 | 58 | 59 | function cmp($a, $b) { 60 | if ($a['bid_total'] == $b['bid_total']) { 61 | return 0; 62 | } 63 | return ($a['bid_total'] > $b['bid_total']) ? -1 : 1; 64 | } 65 | 66 | usort($promotions, "cmp"); 67 | 68 | $header = "| | Amount | Bids | Bidders | Post |\n"; 69 | $header .= "|:--:|:----------------:|:----------:|:----------:|:----------:|\n"; 70 | $body = ''; 71 | 72 | $number_to_show = 100; 73 | $analysis = array(); 74 | for ($i = 0; $i < 125; $i = $i+5) { 75 | $analysis[$i] = 0; 76 | } 77 | 78 | $count = 0; 79 | foreach ($promotions as $key => $promotion) { 80 | $count++; 81 | if ($count <= $number_to_show) { 82 | $body .= '| ' . $count . ' | ' . $promotion['bid_total'] . ' | ' . $promotion['bid_count'] . ' | '; 83 | foreach ($promotion['bidders'] as $bidder => $amount) { 84 | $body .= $bidder . ': ' . $amount . "
"; 85 | } 86 | $body .= '| ' . $promotion['permlink'] . ' |' . "\n"; 87 | } 88 | foreach ($analysis as $key => $value) { 89 | if ($promotion['bid_total'] >= $key && $promotion['bid_total'] < $key+5) { 90 | $analysis[$key] += 1; 91 | } 92 | } 93 | } 94 | 95 | print $header . $body; 96 | 97 | print "\n\n"; 98 | 99 | 100 | print "Bid Amount,Number of Bids\n"; 101 | foreach ($analysis as $key => $value) { 102 | print $key . "-" . ($key+5) . "," . $value . "\n"; 103 | } 104 | 105 | //var_dump($analysis); 106 | -------------------------------------------------------------------------------- /tests/Reporting/steemmonsters.php: -------------------------------------------------------------------------------- 1 | array(), 2 => array(), 3 => array(), 4 => array()); 598 | foreach ($steem_monster_data as $i => $data) { 599 | $steem_monsters[$data['rarity']][$data['id']] = $data['name']; 600 | } 601 | 602 | require '../../vendor/autoload.php'; 603 | ini_set('memory_limit', '1G'); 604 | $SteemServiceLayer = new \SteemTools\SteemServiceLayer(); 605 | $api = new \SteemTools\SteemAPI($SteemServiceLayer); 606 | $account = 'steemmonsters'; 607 | $limit = 2000; 608 | $start = '2018-05-01'; 609 | $end = '2018-06-30'; 610 | 611 | $params = array($account, -1, $limit); 612 | //$result = $api->getAccountHistory($params); 613 | $result = $api->cachedCall('getAccountHistory', $params, true); 614 | $info = getResultInfo($result); 615 | $start_timestamp = strtotime($start); 616 | $end_timestamp = strtotime($end); 617 | $filtered_results = filterAccountHistory($result,$start_timestamp,$end_timestamp,array('custom_json')); 618 | //var_dump($start_timestamp); 619 | //var_dump($info['min_timestamp']); 620 | while ($start_timestamp < $info['min_timestamp']) { 621 | $from = $info['min_id']; 622 | if ($limit > $from) { 623 | $limit = $from; 624 | } 625 | //var_dump($from); 626 | //var_dump($limit); 627 | if ($limit == $from && $from == 0) { 628 | break; 629 | } 630 | $params = array($account, $info['min_id'], $limit); 631 | //$result = $api->getAccountHistory($params); 632 | $result = $api->cachedCall('getAccountHistory', $params, true); 633 | $filtered_results = array_merge( 634 | $filtered_results, 635 | filterAccountHistory($result,$start_timestamp,$end_timestamp,array('custom_json')) 636 | ); 637 | $info = getResultInfo($result); 638 | //var_dump($start_timestamp); 639 | //var_dump($info['min_timestamp']); 640 | } 641 | 642 | $custom_json_blocks = $filtered_results; 643 | $custom_json_data = getCustomJsonData($custom_json_blocks); 644 | $pack_data = filterBySMJsonId($custom_json_data,'generate_packs'); 645 | $accounts = array(); 646 | $card_count = array(); 647 | foreach ($pack_data as $i => $data) { 648 | //print '.'; 649 | if (!array_key_exists($data['account'], $accounts)) { 650 | $accounts[$data['account']] = array('total' => 0); 651 | } 652 | foreach ($data['packs'] as $i => $pack_data) { 653 | foreach ($pack_data[1] as $i => $card_data) { 654 | if (!array_key_exists($card_data[0], $card_count)) { 655 | $card_count[$card_data[0]] = 0; 656 | } 657 | $card_count[$card_data[0]]++; 658 | if (!array_key_exists($card_data[0], $accounts[$data['account']])) { 659 | $accounts[$data['account']][$card_data[0]] = 0; 660 | } 661 | $accounts[$data['account']][$card_data[0]]++; 662 | $accounts[$data['account']]['total']++; 663 | } 664 | } 665 | ksort($accounts[$data['account']]); 666 | } 667 | 668 | ksort($card_count); 669 | 670 | //var_dump($accounts); 671 | 672 | uasort($accounts, function($a, $b) { 673 | return $a['total'] - $b['total']; 674 | }); 675 | 676 | $total_purchasers = count($accounts); 677 | 678 | print "Top 50 Most Addicted Accounts\n"; 679 | print "| Addiction Rank | Account | Card Purchases | \n"; 680 | print "|:----------------:|:----------------:|:------------------------:|\n"; 681 | 682 | for($i = 1; $i <= 50; $i++) { 683 | $keys = array_keys($accounts); 684 | $last_account = array_pop($keys); 685 | print "|" . $i . "|" . $last_account . "|" . number_format($accounts[$last_account]['total']) . "|\n"; 686 | array_pop($accounts); 687 | } 688 | 689 | $i = 50; 690 | while (count($accounts) > 0) { 691 | $i++; 692 | $keys = array_keys($accounts); 693 | $last_account = array_pop($keys); 694 | if ($last_account == 'lukes.random') { 695 | print "\nI'm currently the " . $i . " most addicted steemmonsters user out of " . $total_purchasers . "\n"; 696 | break; 697 | } 698 | array_pop($accounts); 699 | } 700 | 701 | //array_values($sorted_accounts)[0]; 702 | 703 | //var_dump($sorted_accounts[0]) 704 | 705 | //var_dump($accounts['lukes.random']); 706 | //var_dump($card_count); 707 | 708 | $rarities = array(1 => 'Common', 2 => 'Rare', 3 => 'Epic', 4 => 'Legendary'); 709 | 710 | foreach ($steem_monsters as $rarity => $group) { 711 | print '

' . $rarities[$rarity] . "

\n"; 712 | print "| Card | Total in Existence | \n"; 713 | print "|:----------------:|:------------------------:|\n"; 714 | foreach ($group as $id => $name) { 715 | print '|'. $steem_monsters[$rarity][$id] . ' | ' . number_format($card_count[$id]) . "|\n"; 716 | } 717 | } 718 | 719 | 720 | //var_dump($custom_json_data); 721 | 722 | /* 723 | $unique_ids = array(); 724 | foreach ($custom_json_data as $i => $data) { 725 | if (!in_array($data['id'], $unique_ids)) { 726 | $unique_ids[] = $data['id']; 727 | } 728 | } 729 | var_dump($unique_ids); 730 | array(8) { 731 | [0]=> 732 | string(14) "combine_result" 733 | [1]=> 734 | string(14) "generate_packs" 735 | [2]=> 736 | string(10) "gift_packs" 737 | [3]=> 738 | string(10) "gift_cards" 739 | [4]=> 740 | string(21) "generate_starter_pack" 741 | [5]=> 742 | string(13) "sm_gift_cards" 743 | [6]=> 744 | string(13) "sm_gift_packs" 745 | [7]=> 746 | string(6) "follow" 747 | } 748 | */ 749 | 750 | 751 | 752 | ////////////////////////////////////////////////////////////////////// 753 | 754 | function getResultInfo($blocks) 755 | { 756 | $max_timestamp = 0; 757 | $min_timestamp = 0; 758 | $max_id = 0; 759 | $min_id = 0; 760 | foreach($blocks as $block) { 761 | $timestamp = strtotime($block[1]['timestamp']); 762 | if ($timestamp >= $max_timestamp) { 763 | $max_timestamp = $timestamp; 764 | $max_id = $block[0]; 765 | } 766 | if (!$min_timestamp) { 767 | $min_timestamp = $max_timestamp; 768 | } 769 | if ($timestamp <= $min_timestamp) { 770 | $min_timestamp = $timestamp; 771 | $min_id = $block[0]; 772 | } 773 | } 774 | return array( 775 | 'max_id' => $max_id, 776 | 'max_timestamp' => $max_timestamp, 777 | 'min_id' => $min_id, 778 | 'min_timestamp' => $min_timestamp 779 | ); 780 | } 781 | 782 | function filterBySMJsonId($custom_json_data, $id) 783 | { 784 | $sm_json_data = array(); 785 | foreach ($custom_json_data as $i => $data) { 786 | //print '.'; 787 | if ($data['id'] == $id) { 788 | $sm_json_data[] = json_decode($data['json'],true); 789 | } 790 | } 791 | return $sm_json_data; 792 | } 793 | 794 | function getCustomJsonData($result) 795 | { 796 | $custom_json_data = array(); 797 | if (count($result)) { 798 | foreach($result as $block) { 799 | $custom_json_data[] = $block[1]['op'][1]; 800 | } 801 | } 802 | return $custom_json_data; 803 | } 804 | 805 | 806 | function filterAccountHistory($result, $start_timestamp, $end_timestamp, $ops = array()) 807 | { 808 | // globals: 809 | $min_timestamp = time(); 810 | $max_timestamp = 0; 811 | $filtered_results = array(); 812 | if (count($result)) { 813 | foreach($result as $block) { 814 | $timestamp = strtotime($block[1]['timestamp']); 815 | if (in_array($block[1]['op'][0], $ops) 816 | && $timestamp >= $start_timestamp 817 | && $timestamp <= $end_timestamp 818 | ) { 819 | $min_timestamp = min($min_timestamp,$timestamp); 820 | $max_timestamp = max($max_timestamp,$timestamp); 821 | $filtered_results[] = $block; 822 | } 823 | } 824 | } 825 | return $filtered_results; 826 | } 827 | 828 | -------------------------------------------------------------------------------- /tests/Reporting/steemmonsters_sales.php: -------------------------------------------------------------------------------- 1 | array(), 2 => array(), 3 => array(), 4 => array()); 598 | foreach ($steem_monster_data as $i => $data) { 599 | $steem_monsters[$data['rarity']][$data['id']] = $data['name']; 600 | } 601 | 602 | require '../../vendor/autoload.php'; 603 | ini_set('memory_limit', '1G'); 604 | //$SteemServiceLayer = new \SteemTools\SteemServiceLayer(array('webservice_url' => 'https://wallet.steem.ws', 'debug' => true)); 605 | $SteemServiceLayer = new \SteemTools\SteemServiceLayer(); 606 | $api = new \SteemTools\SteemAPI($SteemServiceLayer); 607 | $account = 'steemmonsters'; 608 | $limit = 2000; 609 | $start = '2018-07-12'; 610 | $end = '2018-07-19'; 611 | 612 | print "\n\nBegin time: " . date('c') . "\n\n"; 613 | 614 | $params = array($account, -1, $limit); 615 | $result = $api->getAccountHistory($params); 616 | //$result = $api->cachedCall('getAccountHistory', $params, true); 617 | $info = getResultInfo($result); 618 | $start_timestamp = strtotime($start); 619 | $end_timestamp = strtotime($end); 620 | 621 | $filtered_results = filterAccountHistory($result,$start_timestamp,$end_timestamp,array('transfer')); 622 | //var_dump($start_timestamp); 623 | //var_dump($info['min_timestamp']); 624 | while ($start_timestamp < $info['min_timestamp']) { 625 | $from = $info['min_id']; 626 | if ($limit > $from) { 627 | $limit = $from; 628 | } 629 | //var_dump($from); 630 | //var_dump($limit); 631 | if ($limit == $from && $from == 0) { 632 | break; 633 | } 634 | $params = array($account, $info['min_id'], $limit); 635 | $result = $api->getAccountHistory($params); 636 | //$result = $api->cachedCall('getAccountHistory', $params, true); 637 | $filtered_results = array_merge( 638 | $filtered_results, 639 | filterAccountHistory($result,$start_timestamp,$end_timestamp,array('transfer')) 640 | ); 641 | $info = getResultInfo($result); 642 | //var_dump($start_timestamp); 643 | //var_dump($info['min_timestamp']); 644 | } 645 | 646 | $transfers = getTransfers($filtered_results); 647 | $sales = array(); 648 | foreach ($transfers as $transfer) { 649 | $memo_details = explode(':',$transfer['memo']); 650 | if (isset($memo_details[0]) && $memo_details[0] == 'sm_market_sale') { 651 | $sale = array( 652 | 'seller' => $transfer['to'], 653 | 'buyer' => $memo_details[2], 654 | 'transaction_id' => $memo_details[1], 655 | 'sale_amount' => $transfer['amount'], 656 | 'sale_currency' => $transfer['currency'] 657 | ); 658 | 659 | 660 | // doesn't work as I don't have a full node with transaction id indexing on :( 661 | //$result = $SteemServiceLayer->call('get_transaction', array($sale['transaction_id'])); 662 | //var_dump($result); 663 | //die(); 664 | 665 | // instead we get hacky and screnscrape steemd. :( 666 | $html = file_get_contents('https://steemd.com/tx/' . $sale['transaction_id']); 667 | $doc = new \DOMDocument(); 668 | @$doc->loadHTML($html); 669 | $xpath = new \DOMXpath($doc); 670 | $nodeList = $xpath->query('//span[@class="action"]'); 671 | $text = $nodeList[0]->textContent; 672 | $search_key = 'sm_sell_cardsjson{"cards":["'; 673 | $pos = strpos($text, $search_key); 674 | $card = ''; 675 | if ($pos !== false) { 676 | $pos = $pos + strlen($search_key); 677 | $end_pos = strpos($text, '"', $pos); 678 | $card = substr($text, $pos, ($end_pos-$pos)); 679 | } 680 | $sale['card'] = $card; 681 | $sales[] = $sale; 682 | } 683 | } 684 | 685 | //var_dump($sales); 686 | 687 | $price_history = $api->getPriceHistoryInUSD('2018-07-14', '2018-07-19'); 688 | 689 | $total_sales_STEEM = 0; 690 | $total_sales_SBD = 0; 691 | $total_sales_USD = 0; 692 | 693 | $sellers = array(); 694 | $buyers = array(); 695 | 696 | $highest_sale_price = 0; 697 | 698 | foreach ($sales as $sale) { 699 | if (!array_key_exists($sale['buyer'], $buyers)) { 700 | $buyers[$sale['buyer']] = array('purchases' => 0, 'amount_usd' => 0, 'average_sale_price' => 0, 'highest_sale_price' => 0); 701 | } 702 | if (!array_key_exists($sale['seller'], $sellers)) { 703 | $sellers[$sale['seller']] = array('sales' => 0, 'amount_usd' => 0, 'average_sale_price' => 0, 'highest_sale_price' => 0); 704 | } 705 | 706 | $usd_value = 0; 707 | if ($sale['sale_currency'] == 'STEEM') { 708 | $total_sales_STEEM += $sale['sale_amount']; 709 | $usd_value = ($price_history['average']['STEEM'] * $sale['sale_amount']); 710 | } else { 711 | $total_sales_SBD += $sale['sale_amount']; 712 | $usd_value = ($price_history['average']['SBD'] * $sale['sale_amount']); 713 | } 714 | $total_sales_USD += $usd_value; 715 | 716 | $highest_sale_price = max($highest_sale_price,$usd_value); 717 | 718 | $sellers[$sale['seller']]['sales']++; 719 | $sellers[$sale['seller']]['amount_usd'] += $usd_value; 720 | $sellers[$sale['seller']]['average_sale_price'] = $sellers[$sale['seller']]['amount_usd'] / $sellers[$sale['seller']]['sales']; 721 | $sellers[$sale['seller']]['highest_sale_price'] = max($sellers[$sale['seller']]['highest_sale_price'], $usd_value); 722 | 723 | $buyers[$sale['buyer']]['purchases']++; 724 | $buyers[$sale['buyer']]['amount_usd'] += $usd_value; 725 | $buyers[$sale['buyer']]['average_sale_price'] = $buyers[$sale['buyer']]['amount_usd'] / $buyers[$sale['buyer']]['purchases']; 726 | $buyers[$sale['buyer']]['highest_sale_price'] = max($buyers[$sale['buyer']]['highest_sale_price'], $usd_value); 727 | 728 | } 729 | 730 | print "**Number of Sales:** " . count($sales) . "\n"; 731 | print "**Total STEEM Sales:** " . number_format($total_sales_STEEM,4) . "\n"; 732 | print "**Total SBD Sales:** " . number_format($total_sales_SBD,4) . "\n"; 733 | print "**USD Value of all Sales:** $" . number_format($total_sales_USD,2) . "\n"; 734 | print "**Average Sale Price:** $" . number_format($total_sales_USD/count($sales),2) . "\n"; 735 | print "**Highest Value Trade:** $" . number_format($highest_sale_price,2) . "\n"; 736 | 737 | uasort($sellers, function($a, $b) { 738 | return $a['amount_usd'] - $b['amount_usd']; 739 | }); 740 | 741 | uasort($buyers, function($a, $b) { 742 | return $a['amount_usd'] - $b['amount_usd']; 743 | }); 744 | 745 | print "\n\n## Top 50 of " . count($sellers) . " Sellers\n"; 746 | 747 | print "| Rank | Seller | Number of Sales | Total Sales in USD | Average Sales Price | Highest Sale Price | \n"; 748 | print "|:------:|:----------------:|:----------------:|:----------------:|:----------------:|:------------------------:|\n"; 749 | 750 | for($i = 1; $i <= 50; $i++) { 751 | $keys = array_keys($sellers); 752 | $last_account = array_pop($keys); 753 | $seller_details = $sellers[$last_account]; 754 | print "|" . $i . "|"; 755 | print "|" . $last_account . "|"; 756 | print $seller_details['sales'] . "|"; 757 | print '$' . number_format($seller_details['amount_usd'],2) . "|"; 758 | print '$' . number_format($seller_details['average_sale_price'],2) . "|"; 759 | print '$' . number_format($seller_details['highest_sale_price'],2) . "|"; 760 | print "\n"; 761 | array_pop($sellers); 762 | } 763 | 764 | print "\n\n## Top 50 of " . count($buyers) . " Buyers\n"; 765 | 766 | print "| Rank | Buyer | Number of Purchases | Total Purchases in USD | Average Purchase Price | Highest Purchase Price | \n"; 767 | print "|:------:|:----------------:|:----------------:|:----------------:|:----------------:|:------------------------:|\n"; 768 | 769 | for($i = 1; $i <= 50; $i++) { 770 | $keys = array_keys($buyers); 771 | $last_account = array_pop($keys); 772 | $buyer_details = $buyers[$last_account]; 773 | print "|" . $i . "|"; 774 | print "|" . $last_account . "|"; 775 | print $buyer_details['purchases'] . "|"; 776 | print '$' . number_format($buyer_details['amount_usd'],2) . "|"; 777 | print '$' . number_format($buyer_details['average_sale_price'],2) . "|"; 778 | print '$' . number_format($buyer_details['highest_sale_price'],2) . "|"; 779 | print "\n"; 780 | array_pop($buyers); 781 | } 782 | 783 | print "\n\nEnd time: " . date('c') . "\n\n"; 784 | 785 | $sales_for_writing_to_file = serialize($sales); 786 | $filename = 'cache/sales_' . md5($sales_for_writing_to_file) . '_data.txt'; 787 | file_put_contents($filename,$sales_for_writing_to_file); 788 | 789 | //var_dump($sellers); 790 | 791 | /* 792 | $custom_json_blocks = $filtered_results; 793 | $custom_json_data = getCustomJsonData($custom_json_blocks); 794 | $pack_data = filterBySMJsonId($custom_json_data,'generate_packs'); 795 | $accounts = array(); 796 | $card_count = array(); 797 | foreach ($pack_data as $i => $data) { 798 | //print '.'; 799 | if (!array_key_exists($data['account'], $accounts)) { 800 | $accounts[$data['account']] = array('total' => 0); 801 | } 802 | foreach ($data['packs'] as $i => $pack_data) { 803 | foreach ($pack_data[1] as $i => $card_data) { 804 | if (!array_key_exists($card_data[0], $card_count)) { 805 | $card_count[$card_data[0]] = 0; 806 | } 807 | $card_count[$card_data[0]]++; 808 | if (!array_key_exists($card_data[0], $accounts[$data['account']])) { 809 | $accounts[$data['account']][$card_data[0]] = 0; 810 | } 811 | $accounts[$data['account']][$card_data[0]]++; 812 | $accounts[$data['account']]['total']++; 813 | } 814 | } 815 | ksort($accounts[$data['account']]); 816 | } 817 | 818 | ksort($card_count); 819 | 820 | //var_dump($accounts); 821 | 822 | uasort($accounts, function($a, $b) { 823 | return $a['total'] - $b['total']; 824 | }); 825 | 826 | $total_purchasers = count($accounts); 827 | 828 | print "Top 50 Most Addicted Accounts\n"; 829 | print "| Addiction Rank | Account | Card Purchases | \n"; 830 | print "|:----------------:|:----------------:|:------------------------:|\n"; 831 | 832 | for($i = 1; $i <= 50; $i++) { 833 | $keys = array_keys($accounts); 834 | $last_account = array_pop($keys); 835 | print "|" . $i . "|" . $last_account . "|" . number_format($accounts[$last_account]['total']) . "|\n"; 836 | array_pop($accounts); 837 | } 838 | 839 | $i = 50; 840 | while (count($accounts) > 0) { 841 | $i++; 842 | $keys = array_keys($accounts); 843 | $last_account = array_pop($keys); 844 | if ($last_account == 'lukes.random') { 845 | print "\nI'm currently the " . $i . " most addicted steemmonsters user out of " . $total_purchasers . "\n"; 846 | break; 847 | } 848 | array_pop($accounts); 849 | } 850 | 851 | //array_values($sorted_accounts)[0]; 852 | 853 | //var_dump($sorted_accounts[0]) 854 | 855 | //var_dump($accounts['lukes.random']); 856 | //var_dump($card_count); 857 | 858 | $rarities = array(1 => 'Common', 2 => 'Rare', 3 => 'Epic', 4 => 'Legendary'); 859 | 860 | foreach ($steem_monsters as $rarity => $group) { 861 | print '

' . $rarities[$rarity] . "

\n"; 862 | print "| Card | Total in Existence | \n"; 863 | print "|:----------------:|:------------------------:|\n"; 864 | foreach ($group as $id => $name) { 865 | print '|'. $steem_monsters[$rarity][$id] . ' | ' . number_format($card_count[$id]) . "|\n"; 866 | } 867 | } 868 | */ 869 | 870 | //var_dump($custom_json_data); 871 | 872 | /* 873 | $unique_ids = array(); 874 | foreach ($custom_json_data as $i => $data) { 875 | if (!in_array($data['id'], $unique_ids)) { 876 | $unique_ids[] = $data['id']; 877 | } 878 | } 879 | var_dump($unique_ids); 880 | array(8) { 881 | [0]=> 882 | string(14) "combine_result" 883 | [1]=> 884 | string(14) "generate_packs" 885 | [2]=> 886 | string(10) "gift_packs" 887 | [3]=> 888 | string(10) "gift_cards" 889 | [4]=> 890 | string(21) "generate_starter_pack" 891 | [5]=> 892 | string(13) "sm_gift_cards" 893 | [6]=> 894 | string(13) "sm_gift_packs" 895 | [7]=> 896 | string(6) "follow" 897 | } 898 | */ 899 | 900 | 901 | 902 | ////////////////////////////////////////////////////////////////////// 903 | 904 | function getResultInfo($blocks) 905 | { 906 | $max_timestamp = 0; 907 | $min_timestamp = 0; 908 | $max_id = 0; 909 | $min_id = 0; 910 | foreach($blocks as $block) { 911 | $timestamp = strtotime($block[1]['timestamp']); 912 | if ($timestamp >= $max_timestamp) { 913 | $max_timestamp = $timestamp; 914 | $max_id = $block[0]; 915 | } 916 | if (!$min_timestamp) { 917 | $min_timestamp = $max_timestamp; 918 | } 919 | if ($timestamp <= $min_timestamp) { 920 | $min_timestamp = $timestamp; 921 | $min_id = $block[0]; 922 | } 923 | } 924 | return array( 925 | 'max_id' => $max_id, 926 | 'max_timestamp' => $max_timestamp, 927 | 'min_id' => $min_id, 928 | 'min_timestamp' => $min_timestamp 929 | ); 930 | } 931 | 932 | function getTransfers($result) 933 | { 934 | $transfers = array(); 935 | foreach($result as $block) { 936 | $transfer = $block[1]['op'][1]; 937 | list($amount, $currency) = sscanf($transfer['amount'], "%f %s"); 938 | $transfer['amount'] = $amount; 939 | $transfer['currency'] = $currency; 940 | $transfers[] = $transfer; 941 | } 942 | return $transfers; 943 | } 944 | 945 | 946 | function filterAccountHistory($result, $start_timestamp, $end_timestamp, $ops = array()) 947 | { 948 | // globals: 949 | $min_timestamp = time(); 950 | $max_timestamp = 0; 951 | $filtered_results = array(); 952 | if (count($result)) { 953 | foreach($result as $block) { 954 | $timestamp = strtotime($block[1]['timestamp']); 955 | if (in_array($block[1]['op'][0], $ops) 956 | && $timestamp >= $start_timestamp 957 | && $timestamp <= $end_timestamp 958 | ) { 959 | $min_timestamp = min($min_timestamp,$timestamp); 960 | $max_timestamp = max($max_timestamp,$timestamp); 961 | $filtered_results[] = $block; 962 | } 963 | } 964 | } 965 | return $filtered_results; 966 | } 967 | 968 | -------------------------------------------------------------------------------- /tests/Reporting/steemmonsters_sales_breakdown.php: -------------------------------------------------------------------------------- 1 | array(), 2 => array(), 3 => array(), 4 => array()); 602 | foreach ($steem_monster_data as $i => $data) { 603 | $steem_monsters[$data['rarity']][$data['id']] = $data['name']; 604 | } 605 | 606 | require '../../vendor/autoload.php'; 607 | ini_set('memory_limit', '1G'); 608 | $SteemServiceLayer = new \SteemTools\SteemServiceLayer(); 609 | $api = new \SteemTools\SteemAPI($SteemServiceLayer); 610 | 611 | 612 | $filename = 'cache/sales_34d6d423242241b50ebfe745247b3963_data.txt'; 613 | $sales = file_get_contents($filename); 614 | $sales = unserialize($sales); 615 | 616 | $cards = array(); 617 | foreach ($sales as $trade) { 618 | $cards[] = $trade['card']; 619 | } 620 | 621 | // get details 622 | print "Number of cards: " . count($cards) . "\n"; 623 | 624 | $get_fresh_card_data = false; 625 | 626 | if ($get_fresh_card_data) { 627 | $url = "https://steemmonsters.com/cards/find?ids="; 628 | 629 | $cards_copy = $cards; 630 | 631 | $number_of_cards = count($cards); 632 | 633 | $all_trade_data = array(); 634 | 635 | while ($number_of_cards > 0) { 636 | $batch = array(); 637 | for ($i=$number_of_cards; $i > ($number_of_cards - 200); $i--) { 638 | if (isset($cards_copy[$i])) { 639 | $batch[] = $cards_copy[$i]; 640 | } 641 | } 642 | $trade_data = file_get_contents($url . implode(",",$batch)); 643 | $trade_data = json_decode($trade_data, true); 644 | $all_trade_data = array_merge($all_trade_data, $trade_data); 645 | $number_of_cards -= 200; 646 | } 647 | 648 | $trade_data = $all_trade_data; 649 | 650 | $trade_data_for_writing_to_file = serialize($trade_data); 651 | $filename = 'cache/trades_' . md5($trade_data_for_writing_to_file) . '_data.txt'; 652 | file_put_contents($filename,$trade_data_for_writing_to_file); 653 | } else { 654 | $filename = 'cache/trades_141e458c72b9d39b452d4eab3c64e663_data.txt'; 655 | $trade_data = file_get_contents($filename); 656 | $trade_data = unserialize($trade_data); 657 | } 658 | 659 | 660 | print "Result count : " . count($trade_data) . "\n"; 661 | 662 | $card_data = $trade_data; 663 | 664 | $price_history = $api->getPriceHistoryInUSD('2018-07-14', '2018-07-19'); 665 | 666 | $cards = array(); 667 | $gold_cards = array(); 668 | 669 | $combined_cards = 0; 670 | 671 | 672 | //var_dump($card_data[0]); 673 | //exit(); 674 | 675 | $missing_cards = array(); 676 | 677 | function parseCardData($card_data, $sales, $price_history, $gold_cards, &$combined_cards, &$missing_cards) { 678 | $cards = array(); 679 | foreach ($card_data as $card_details) { 680 | if (!isset($card_details['card_detail_id'])) { 681 | $combined_cards++; 682 | $missing_cards[] = $card_details['uid']; 683 | } else { 684 | if ($card_details['gold'] == $gold_cards) { 685 | if (!array_key_exists($card_details['card_detail_id'], $cards)) { 686 | $cards[$card_details['card_detail_id']] = array( 687 | 'name' => $card_details['details']['name'], 688 | 'rarity' => $card_details['details']['rarity'], 689 | 'is_starter' => $card_details['details']['is_starter'], 690 | 'sale_count' => 0, 691 | 'total_sales_usd' => 0, 692 | 'average_sale_price' => 0, 693 | 'highest_sale_price' => 0, 694 | 'lowest_sale_price' => 9999999999999999, 695 | 'trades' => array(), 696 | ); 697 | } 698 | $trade = array(); 699 | foreach ($sales as $trade_details) { 700 | if ($trade_details['card'] == $card_details['uid']) { 701 | $cards[$card_details['card_detail_id']]['sale_count']++; 702 | $usd_value = 0; 703 | if ($trade_details['sale_currency'] == 'STEEM') { 704 | $usd_value = ($price_history['average']['STEEM'] * $trade_details['sale_amount']); 705 | } else { 706 | $usd_value = ($price_history['average']['SBD'] * $trade_details['sale_amount']); 707 | } 708 | $cards[$card_details['card_detail_id']]['total_sales_usd'] += $usd_value; 709 | $cards[$card_details['card_detail_id']]['highest_sale_price'] = max($cards[$card_details['card_detail_id']]['highest_sale_price'],$usd_value); 710 | $cards[$card_details['card_detail_id']]['lowest_sale_price'] = min($cards[$card_details['card_detail_id']]['lowest_sale_price'],$usd_value); 711 | $cards[$card_details['card_detail_id']]['average_sale_price'] = $cards[$card_details['card_detail_id']]['total_sales_usd'] / $cards[$card_details['card_detail_id']]['sale_count']; 712 | $trade[] = $trade_details; 713 | } 714 | } 715 | $cards[$card_details['card_detail_id']]['trades'][] = $trade; 716 | } 717 | } 718 | } 719 | return $cards; 720 | } 721 | 722 | 723 | $cards = parseCardData($card_data,$sales,$price_history,false,$combined_cards,$missing_cards); 724 | $gold_cards = parseCardData($card_data,$sales,$price_history,true,$combined_cards,$missing_cards); 725 | 726 | print "## Gold Card Sales\n"; 727 | 728 | $header = "| Name | Sales Count | Total Sales | Average | Max | Min | \n"; 729 | $header .= "|:------:|:----------------:|:----------------:|:----------------:|:----------------:|:------------------------:|\n"; 730 | 731 | $rarities = array(1 => 'Common', 2 => 'Rare', 3 => 'Epic', 4 => 'Legendary'); 732 | $last_rarity = ''; 733 | 734 | foreach ($steem_monsters as $rarity => $group) { 735 | if ($last_rarity != $rarity) { 736 | print '

' . $rarities[$rarity] . "

\n"; 737 | print $header; 738 | $last_rarity = $rarity; 739 | } 740 | foreach ($group as $group_card_id => $name) { 741 | foreach ($gold_cards as $id => $card) { 742 | if ($group_card_id == $id) { 743 | print "| " . $card['name'] . " | "; 744 | print $card['sale_count'] . " | "; 745 | print "$" . number_format($card['total_sales_usd'],2) . " | "; 746 | print "$" . number_format($card['average_sale_price'],2) . " | "; 747 | print "$" . number_format($card['highest_sale_price'],2) . " | "; 748 | print "$" . number_format($card['lowest_sale_price'],2) . " | "; 749 | print "\n"; 750 | } 751 | } 752 | } 753 | } 754 | 755 | print "## Regular Card Sales\n"; 756 | 757 | $header = "| Name | Sales Count | Total Sales | Average | Max | Min | \n"; 758 | $header .= "|:------:|:----------------:|:----------------:|:----------------:|:----------------:|:------------------------:|\n"; 759 | 760 | $last_rarity = ''; 761 | 762 | foreach ($steem_monsters as $rarity => $group) { 763 | if ($last_rarity != $rarity) { 764 | print '

' . $rarities[$rarity] . "

\n"; 765 | print $header; 766 | $last_rarity = $rarity; 767 | } 768 | foreach ($group as $group_card_id => $name) { 769 | foreach ($cards as $id => $card) { 770 | if ($group_card_id == $id) { 771 | print "| " . $card['name'] . " | "; 772 | print $card['sale_count'] . " | "; 773 | print "$" . number_format($card['total_sales_usd'],2) . " | "; 774 | print "$" . number_format($card['average_sale_price'],2) . " | "; 775 | print "$" . number_format($card['highest_sale_price'],2) . " | "; 776 | print "$" . number_format($card['lowest_sale_price'],2) . " | "; 777 | print "\n"; 778 | } 779 | } 780 | } 781 | } 782 | 783 | print "\nThere were " . $combined_cards . " trades for cards that were combined after the trade so their information is gone.\n"; 784 | 785 | //var_dump($missing_cards); 786 | 787 | -------------------------------------------------------------------------------- /tests/Reporting/votes.php: -------------------------------------------------------------------------------- 1 | getAccountVotes($account); 12 | 13 | $vote_history = array(); 14 | 15 | print ''; 16 | print count($votes) . ' votes returned.
Collecting information on each vote.

'; 17 | 18 | $votes_to_show = 9999; 19 | $votes_collected = 0; 20 | foreach($votes as $vote) { 21 | if (strpos($vote['authorperm'], '/re-') === false) { 22 | $votes_collected++; 23 | list($author,$permlink,$original) = explode('/',$vote['authorperm'], 3); 24 | $content = $steemd->getContent($author,$permlink); 25 | $vote_history[] = array( 26 | 'author' => $content['author'], 27 | 'url' => $content['url'], 28 | 'title' => $content['title'], 29 | 'vote_time' => $vote['time'], 30 | ); 31 | print '.'; 32 | if ($votes_collected >= $votes_to_show) { 33 | break; 34 | } 35 | } 36 | } 37 | 38 | sortBy('vote_time',$vote_history); 39 | 40 | function sortBy($field, &$array, $direction = 'desc') 41 | { 42 | usort($array, create_function('$a, $b', ' 43 | $a = $a["' . $field . '"]; 44 | $b = $b["' . $field . '"]; 45 | 46 | if ($a == $b) 47 | { 48 | return 0; 49 | } 50 | 51 | return ($a ' . ($direction == 'desc' ? '>' : '<') .' $b) ? -1 : 1; 52 | ')); 53 | } 54 | 55 | print ''; 56 | print ''; 57 | print ''; 58 | print ''; 59 | print ''; 60 | print ''; 61 | foreach ($vote_history as $record) { 62 | print ''; 63 | print ''; 66 | print ''; 69 | print ''; 72 | print ''; 73 | } 74 | print '
AuthorTitleVote Time
'; 64 | print '@' . $record['author'] . ''; 65 | print ''; 67 | print '' . $record['title'] . ''; 68 | print ''; 70 | print $record['vote_time']; 71 | print '
'; 75 | print ''; 76 | -------------------------------------------------------------------------------- /tests/SteemAPITest.php: -------------------------------------------------------------------------------- 1 | getCurrentMedianHistoryPrice(); 9 | 10 | var_dump($result); 11 | --------------------------------------------------------------------------------