From 13d24550eab195c7b88ca1c3cc4e7690042c5c89 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 8 Jul 2018 23:52:19 +0300 Subject: [PATCH 001/193] Initial commit --- LICENSE | 21 +++++++++++++++++++++ README.md | 2 ++ 2 files changed, 23 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8f1d118 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Mohammad Farhat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e5c6be --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# actifitbot +script bot handling upvotes and token calculation for actifit From 0c19ab2775d6855550bc4cce46d8e2a1f4c0e1f4 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 8 Jul 2018 23:55:11 +0300 Subject: [PATCH 002/193] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e5c6be..b327346 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# actifitbot -script bot handling upvotes and token calculation for actifit +# actifit-bot +This repo contains script that allows upvoting as well as the calculation of the relevant actifit tokens due to posting, upvoting, and resteeming From fa8eb7d34c848d21a071078f8e58c0a79c6dba51 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Sun, 8 Jul 2018 18:55:49 -0300 Subject: [PATCH 003/193] added config file for heroku --- .gitignore | 1 - config.json | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 config.json diff --git a/.gitignore b/.gitignore index 69026f6..751e374 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ logs *.log npm-debug.log* -config.json state.json # Runtime data diff --git a/config.json b/config.json new file mode 100644 index 0000000..b6880df --- /dev/null +++ b/config.json @@ -0,0 +1,26 @@ +{ + "disabled_mode": false, + "testing": true, + "detailed_logging": false, + "account": "actifit", + "main_tag": "actifit", + "memo_key": "your_private_memo_key", + "posting_key": "your_private_posting_key", + "active_key": "your_private_active_key", + "auto_claim_rewards" : false, + "vote_weight": 100000, + "min_hours": 1, + "comment_location": "comment.md", + "comment": false, + "resteem": false, + "no_paid_bots": false, + "beneficiaries": ["actifit", "actifit.pay"], + "flag_signal_accounts": ["spaminator", "cheetah", "steemcleaners", "mack-bot"], + "mongo_uri": "mongodb://actifit_user:NTAyY2I5ZTM4YzM3YjRjYmIxYThhYmM3@ds129051.mlab.com:29051/heroku_ch0sdt2p", + "mongo_local": "mongodb://localhost:27017", + "db_name": "heroku_ch0sdt2p", + "report_emails": "urucrypto@gmail.com", + "smtp_usr": "SMTP_Injection", + "smtp_from": "'Comus Bot' ", + "smtp_key": "5eee659ffb12c5f76d1a268718052d81e1b67914" +} From 615a0dbd1940518c64c45a09da0aff139ba380e5 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Sun, 8 Jul 2018 18:57:31 -0300 Subject: [PATCH 004/193] changed root return --- app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 093455d..c741167 100644 --- a/app.js +++ b/app.js @@ -54,7 +54,8 @@ app.get('/', function (req, res) { }]; data.total_votes = 323; data.total_money = "$0.63"; - res.render('home', data); + // res.render('home', data); + res.send('Hello there!'); }); app.get('/user/:user', async function (req, res) { From 85dd01674356ed2b6158f22fc60603361827d602 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Sun, 8 Jul 2018 19:03:43 -0300 Subject: [PATCH 005/193] added user transactions --- app.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app.js b/app.js index c741167..42c300c 100644 --- a/app.js +++ b/app.js @@ -64,6 +64,11 @@ app.get('/user/:user', async function (req, res) { res.send(user); }); +app.get('/user/transactions/:user', async function (req, res) { + let transactions = await db.collection('token_transactions').find({user: req.params.user}).sort({date: -1}).limit(250).toArray(); + res.send(transactions); +}); + app.get('/transactions', async function (req, res) { let transactions = await db.collection('token_transactions').find().sort({date: -1}).limit(250).toArray(); res.send(transactions); From c376d902e714cf55eb11afc09065af24e309cf24 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Mon, 9 Jul 2018 21:46:31 -0300 Subject: [PATCH 006/193] async votes and comments - save data gets reblogs --- app.js | 14 +++--- config.json | 7 +-- curation-bot.js | 112 +++++++++++++++++++++++++++++------------------- save-data.js | 83 ++++++++++++++++++++++++++--------- 4 files changed, 141 insertions(+), 75 deletions(-) diff --git a/app.js b/app.js index 42c300c..f4d4454 100644 --- a/app.js +++ b/app.js @@ -59,18 +59,16 @@ app.get('/', function (req, res) { }); app.get('/user/:user', async function (req, res) { - let user = await collection.findOne({_id: req.params.user}); + let user = await collection.findOne({_id: req.params.user}, {fields : { _id:0} }); console.log(user); res.send(user); }); -app.get('/user/transactions/:user', async function (req, res) { - let transactions = await db.collection('token_transactions').find({user: req.params.user}).sort({date: -1}).limit(250).toArray(); - res.send(transactions); -}); - -app.get('/transactions', async function (req, res) { - let transactions = await db.collection('token_transactions').find().sort({date: -1}).limit(250).toArray(); +app.get('/transactions/:user?', async function (req, res) { + let query = {}; + if(req.params.user) + query = {user: req.params.user} + let transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(250).toArray(); res.send(transactions); }); diff --git a/config.json b/config.json index b6880df..d427d4e 100644 --- a/config.json +++ b/config.json @@ -16,9 +16,10 @@ "no_paid_bots": false, "beneficiaries": ["actifit", "actifit.pay"], "flag_signal_accounts": ["spaminator", "cheetah", "steemcleaners", "mack-bot"], - "mongo_uri": "mongodb://actifit_user:NTAyY2I5ZTM4YzM3YjRjYmIxYThhYmM3@ds129051.mlab.com:29051/heroku_ch0sdt2p", - "mongo_local": "mongodb://localhost:27017", - "db_name": "heroku_ch0sdt2p", + "mongo_remote": "mongodb://actifit_user:NTAyY2I5ZTM4YzM3YjRjYmIxYThhYmM3@ds129051.mlab.com:29051/heroku_ch0sdt2p", + "mongo_uri": "mongodb://localhost:27017", + "db_remote": "heroku_ch0sdt2p", + "db_name": "actifit", "report_emails": "urucrypto@gmail.com", "smtp_usr": "SMTP_Injection", "smtp_from": "'Comus Bot' ", diff --git a/curation-bot.js b/curation-bot.js index 6892131..e6d58c5 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -274,21 +274,26 @@ function processVotes() { function votingProcess(posts, power_per_vote) { // Get the first bid in the list - sendVote(posts.pop(), 2, power_per_vote); - // If there are more bids, vote on the next one after 10 seconds - if (posts.length > 0) { - setTimeout(function () { votingProcess(posts, power_per_vote); }, 5000); - } else { - setTimeout(function () { - utils.log('======================================================='); - utils.log('Voting Complete!'); - utils.log('======================================================='); - is_voting = false; - error_sent = false; - saveState(); - //reportEmail(config.report_emails) - }, 5000); - } + sendVote(posts.pop(), 2, power_per_vote) + .then( res => { + // If there are more bids, vote on the next one after 10 seconds + if (posts.length > 0) { + setTimeout(function () { votingProcess(posts, power_per_vote); }, 5000); + } else { + setTimeout(function () { + utils.log('======================================================='); + utils.log('Voting Complete!'); + utils.log('======================================================='); + is_voting = false; + error_sent = false; + saveState(); + //reportEmail(config.report_emails) + }, 5000); + } + }) + .catch(err => { + console.log(err); + }) } function sendVote(post, retries, power_per_vote) { @@ -299,30 +304,45 @@ function sendVote(post, retries, power_per_vote) { post.vote_weight = vote_weight; last_votes.push(post); - steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { - if (!err && result) { - utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); + return new Promise((resolve, reject) => { + steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { + if (!err && result) { + utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); + + if(!true) + //if(config.comment_location && config.comment) + setTimeout(function () { + sendComment(post.author, post.permlink, vote_weight, post.rate_multiplier, post.json.step_count) + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, 10000); + else + resolve(result); + } else { + utils.log(err, result); - if(config.comment_location && config.comment) - setTimeout(function () { sendComment(post.author, post.permlink, vote_weight, post.rate_multiplier, post.json.step_count); }, 10000); - } else { - utils.log(err, result); - - // Try again one time on error - if (retries < 1) - sendVote(post, retries + 1); - else { - var message = '============= Vote transaction failed two times for: ' + post.url + ' ===============' - utils.log(message); - //errorEmail(message, config.report_emails); + // Try again one time on error + if (retries < 1) + sendVote(post, retries + 1); + else { + var message = '============= Vote transaction failed two times for: ' + post.url + ' ===============' + utils.log(message); + reject(err); + //errorEmail(message, config.report_emails); + } } - } + }); }); } function sendComment(parentAuthor, parentPermlink, vote_weight, rate_multiplier, post_step_count) { var content = null; - + // Return promise + return new Promise((resolve, reject) => { content = fs.readFileSync(config.comment_location, "utf8"); // If promotion content is specified in the config then use it to comment on the upvoted post @@ -348,19 +368,23 @@ function sendComment(parentAuthor, parentPermlink, vote_weight, rate_multiplier, // Replace variables in the promotion content content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{milestone\}/g, milestone_txt).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); - // Broadcast the comment - steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, '{"app":"communitybot/' + version + '"}', function (err, result) { - if (!err && result) { - utils.log('Posted comment: ' + permlink); - } else { - utils.log('Error posting comment: ' + permlink); - } - }); - } - + + // Broadcast the comment + steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, '{"app":"communitybot/' + version + '"}', function (err, result) { + if (!err && result) { + utils.log('Posted comment: ' + permlink); + resolve(result); + } else { + utils.log('Error posting comment: ' + permlink); + reject(err); + } + }); + } else + reject('Failed to load content'); +}); // Check if the bot should resteem this post - if (config.resteem) - resteem(parentAuthor, parentPermlink); + /* if (config.resteem) + resteem(parentAuthor, parentPermlink); */ } function reportEmail(to) { diff --git a/save-data.js b/save-data.js index 3bc1a67..915eca6 100644 --- a/save-data.js +++ b/save-data.js @@ -10,7 +10,7 @@ var config = utils.getConfig(); // Connection URL const url = config.mongo_uri; - +var postsProcessing = false; var db; var collection; // Database Name @@ -28,12 +28,13 @@ MongoClient.connect(url, function(err, client) { // Get the documents collection collection = db.collection(collection_name); - // client.close(); - //processVotedPosts(); - updateUserTokens(); - getPosts(); - setInterval(getPosts, 300 * 1000); - setInterval(updateUserTokens, 450 * 1000); + // client.close(); + processVotedPosts(); + //getReblogs(); + //updateUserTokens(); + //getPosts(); + /* setInterval(getPosts, 300 * 1000); + setInterval(updateUserTokens, 450 * 1000);*/ } else { utils.log(err, 'import'); mail.sendPlainMail('Database Error', err, 'cryptouru@gmail.com') @@ -50,6 +51,9 @@ MongoClient.connect(url, function(err, client) { }); function getPosts(index) { + if(postsProcessing) + return; + postsProcessing = true; console.log('---- Getting Posts ----'); var query = {tag: config.main_tag, limit: 100}; if (index) { @@ -106,7 +110,8 @@ function getPosts(index) { console.log('Inserted transactions'); if (!index || (index.start_permlink != last_post.permlink && index.start_author != last_post.author && result.length >= 100)) return getPosts({start_author: last_post.author, start_permlink: last_post.permlink}); - console.log('No more new posts'); + console.log('No more new posts'); + postsProcessing = false; return; }) .catch(function (err) { @@ -146,7 +151,7 @@ async function processTransactions(posts) { posts.forEach(async post => { let post_transaction = { user: post.author, - reward_activty: 'Post', + reward_activity: 'Post', token_count: post.token_rewards, url: post.url, date: post.created @@ -154,7 +159,7 @@ async function processTransactions(posts) { bulk.find( { user: post_transaction.user, - reward_activty: post_transaction.reward_activty, + reward_activity: post_transaction.reward_activity, url: post_transaction.url }) .upsert().replaceOne(post_transaction); @@ -162,7 +167,7 @@ async function processTransactions(posts) { post.active_votes.forEach(async vote => { let vote_transaction = { user: vote.voter, - reward_activty: 'Post Vote', + reward_activity: 'Post Vote', token_count: 1, url: post.url, date: vote.time @@ -170,23 +175,52 @@ async function processTransactions(posts) { bulk.find( { user: vote_transaction.user, - reward_activty: vote_transaction.reward_activty, + reward_activity: vote_transaction.reward_activity, url: vote_transaction.url }) .upsert().replaceOne(vote_transaction); transactions.push(vote_transaction); - }); + }); + let reblogs = await steem.api.getRebloggedByAsync(post.author, post.permlink); + console.log('------------------ REBLOGS --------------------'); + console.log(reblogs); + reblogs.forEach(async reblog => { + if(reblog != post.author){ + let reblog_transaction = { + user: reblog, + reward_activity: 'Post Reblog', + token_count: 1, + url: post.url, + date: post.created + } + console.log('---Reblog transaction ----'); + console.log(reblog_transaction); + bulk.find( + { + user: reblog_transaction.user, + reward_activity: reblog_transaction.reward_activity, + url: reblog_transaction.url + }) + .upsert().replaceOne(reblog_transaction); + transactions.push(reblog_transaction); + } + }); }); return bulk.execute(); } async function updateUserTokens() { console.log('---- Updating Users ----'); - let query = await db.collection('token_transactions').aggregate( - [ - { $group: { _id: "$user", tokens: { $sum: "$token_count" } } }, - { $sort: { tokens: -1 } } - ]); + let query = await db.collection('token_transactions').aggregate([ + { $group: { _id: "$user", tokens: { $sum: "$token_count" } } }, + { $sort: { tokens: -1 } }, + { $project: { + _id: "$_id", + user: "$_id", + tokens: "$tokens", + } + } + ]) let user_tokens = await query.toArray(); await db.collection('user_tokens').drop(); return await db.collection('user_tokens').insert(user_tokens); @@ -201,8 +235,8 @@ async function processVotedPosts() { await utils.asyncForEach(transactions, async (txs) => { await steem.api.getContentAsync(txs.author, txs.permlink) .then(postObject => { - if (utils.checkBeneficiary(postObject)) { - console.log('--- Post has correct beneficiaries -----'); + if (utils.checkBeneficiary(postObject) || postObject.author == config.account) { + console.log('--- Post has correct beneficiaries or was posted by config account -----'); postsData.push(postObject); } else { console.log('--- Post missing beneficiaries -----'); @@ -229,6 +263,14 @@ async function getAccountVotes(account) { return voteByAccount; } +async function getReblogs(post) { + console.log('----- Getting reblogs ----'); + let res; + res = await steem.api.getRebloggedByAsync(post.author, post.permlink); + console.log(res); + return res; +} + async function upsertPosts(posts) { // Upsert posts var bulk = collection.initializeUnorderedBulkOp(); @@ -266,6 +308,7 @@ async function upsertPosts(posts) { var mes = res.nInserted + ' posts inserted - ' + res.nUpserted + ' posts upserted - ' + res.nModified + ' posts updated'; utils.log(mes, 'import'); await processTransactions(posts); + console.log('----Super upsert ready ----'); return; }) .catch(function (err) { From c70d32d8d8a709c966191773e5c25a9327484fda Mon Sep 17 00:00:00 2001 From: Ignacio Date: Tue, 10 Jul 2018 00:08:13 -0300 Subject: [PATCH 007/193] new async function --- package-lock.json | 5 +++++ package.json | 1 + save-data.js | 16 +++++++++------- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 72e33ab..6647fbb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2668,6 +2668,11 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, + "p-iteration": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.7.tgz", + "integrity": "sha512-VsYvUPjm2edbKkX4QzlASC1qB2e4Z6IE9WPaRVHKwCtobmB6vfUcU9eBOwj1k5uMNi8O6w89QfsDatO5ePSjQg==" + }, "package-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", diff --git a/package.json b/package.json index 5e5ed35..b525543 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "mongodb": "^3.0.10", "nodemailer": "^4.6.5", "nodemailer-express-handlebars": "^3.0.0", + "p-iteration": "^1.1.7", "request": "^2.83.0", "steem": "^0.6.7" }, diff --git a/save-data.js b/save-data.js index 915eca6..4abcb26 100644 --- a/save-data.js +++ b/save-data.js @@ -1,6 +1,7 @@ var utils = require('./utils'); var mail = require('./mail'); const steem = require('steem'); +const { forEach } = require('p-iteration'); const MongoClient = require('mongodb').MongoClient; const assert = require('assert'); @@ -29,12 +30,12 @@ MongoClient.connect(url, function(err, client) { collection = db.collection(collection_name); // client.close(); - processVotedPosts(); + //processVotedPosts(); //getReblogs(); - //updateUserTokens(); - //getPosts(); - /* setInterval(getPosts, 300 * 1000); - setInterval(updateUserTokens, 450 * 1000);*/ + updateUserTokens(); + getPosts(); + setInterval(getPosts, 300 * 1000); + setInterval(updateUserTokens, 450 * 1000); } else { utils.log(err, 'import'); mail.sendPlainMail('Database Error', err, 'cryptouru@gmail.com') @@ -148,7 +149,7 @@ async function processTransactions(posts) { let transactions = []; let collection = db.collection('token_transactions'); var bulk = collection.initializeUnorderedBulkOp(); - posts.forEach(async post => { + await forEach(posts, async (post) => { let post_transaction = { user: post.author, reward_activity: 'Post', @@ -206,6 +207,7 @@ async function processTransactions(posts) { } }); }); + console.log(transactions); return bulk.execute(); } @@ -232,7 +234,7 @@ async function processVotedPosts() { let transactions = await getAccountVotes(config.account); let postsData = []; - await utils.asyncForEach(transactions, async (txs) => { + await forEach(transactions, async (txs) => { await steem.api.getContentAsync(txs.author, txs.permlink) .then(postObject => { if (utils.checkBeneficiary(postObject) || postObject.author == config.account) { From 34fb17c1a02d66c12df7c4fb0d8db584f1a0d302 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Tue, 10 Jul 2018 00:13:39 -0300 Subject: [PATCH 008/193] deploy config --- app.js | 9 --------- config.json | 8 ++++---- curation-bot.js | 2 +- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/app.js b/app.js index f4d4454..bdaa59c 100644 --- a/app.js +++ b/app.js @@ -31,15 +31,6 @@ MongoClient.connect(url, function(err, client) { collection = db.collection(collection_name); } else { utils.log(err, 'api'); - mail.sendPlainMail('Database Error', err, 'cryptouru@gmail.com') - .then(function(res, err) { - if (!err) { - console.log(res); - } else { - utils.log(err, 'api'); - } - }); - process.exit(); } }); diff --git a/config.json b/config.json index d427d4e..45b54dc 100644 --- a/config.json +++ b/config.json @@ -16,10 +16,10 @@ "no_paid_bots": false, "beneficiaries": ["actifit", "actifit.pay"], "flag_signal_accounts": ["spaminator", "cheetah", "steemcleaners", "mack-bot"], - "mongo_remote": "mongodb://actifit_user:NTAyY2I5ZTM4YzM3YjRjYmIxYThhYmM3@ds129051.mlab.com:29051/heroku_ch0sdt2p", - "mongo_uri": "mongodb://localhost:27017", - "db_remote": "heroku_ch0sdt2p", - "db_name": "actifit", + "mongo_uri": "mongodb://actifit_user:NTAyY2I5ZTM4YzM3YjRjYmIxYThhYmM3@ds129051.mlab.com:29051/heroku_ch0sdt2p", + "mongo_local": "mongodb://localhost:27017", + "db_name": "heroku_ch0sdt2p", + "db_remote": "actifit", "report_emails": "urucrypto@gmail.com", "smtp_usr": "SMTP_Injection", "smtp_from": "'Comus Bot' ", diff --git a/curation-bot.js b/curation-bot.js index e6d58c5..fce14ad 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -253,7 +253,7 @@ function processVotes() { utils.log(vote_data.power_per_vote * 0.35 + ' power per fith vote.'); utils.log(vote_data.power_per_vote * 0.2 + ' power per lowest vote.'); if(config.testing) - process.exit(); + return; else votingProcess(votePosts, vote_data.power_per_vote); From f7db664eb3224fed8ff505897edebe878dd25d5a Mon Sep 17 00:00:00 2001 From: Ignacio Date: Tue, 10 Jul 2018 01:37:51 -0300 Subject: [PATCH 009/193] fixed user tokens database error --- save-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/save-data.js b/save-data.js index 4abcb26..34f766e 100644 --- a/save-data.js +++ b/save-data.js @@ -224,7 +224,7 @@ async function updateUserTokens() { } ]) let user_tokens = await query.toArray(); - await db.collection('user_tokens').drop(); + await db.collection('user_tokens').remove({}); return await db.collection('user_tokens').insert(user_tokens); } From fc449181ccc50e6ae83c224ef9abefecef5101e1 Mon Sep 17 00:00:00 2001 From: Ignacio Date: Tue, 10 Jul 2018 19:59:31 -0300 Subject: [PATCH 010/193] fixed comment conditional --- curation-bot.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index fce14ad..69033ea 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -309,8 +309,7 @@ function sendVote(post, retries, power_per_vote) { if (!err && result) { utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); - if(!true) - //if(config.comment_location && config.comment) + if(config.comment_location && config.comment) setTimeout(function () { sendComment(post.author, post.permlink, vote_weight, post.rate_multiplier, post.json.step_count) .then( res => { From bdb919deab4a51e201b7ce9d85e48219083f70c9 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 12 Jul 2018 11:33:38 +0300 Subject: [PATCH 011/193] Update README.md --- README.md | 56 +++---------------------------------------------------- 1 file changed, 3 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index a955326..b3fede7 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,5 @@ -# Community Bot - Voting bot for Actifit community on Steem! +# Actifit Bot -This is a voting bot for Actifit. It backups posts, tracks token transactions, user holdings and votes on Actifit posts. +This bot was built to perform voting on actifit posts, but also store and calculate actifit tokens, transactions and relevant posts into own Mongogb -## Installation -``` -$ npm install -``` - -## Configuration -First rename config-example.json to config.json: -``` -$ mv config-example.json config.json -``` - -Then set the following options in config.json: -``` -{ - "disabled_mode": false, - "testing": true, - "detailed_logging": false, - "account": "actifit", - "main_tag": "actifit", - "memo_key": "your_private_memo_key", - "posting_key": "your_private_posting_key", - "active_key": "your_private_active_key", - "auto_claim_rewards" : true, - "vote_weight": 100000, - "min_hours": 24, - "comment_location": "comment.md", - "comment": false, - "resteem": false, - "no_paid_bots": true, - "beneficiaries": ["actifit", "actifit.pay"], - "flag_signal_accounts": ["spaminator", "cheetah", "steemcleaners", "mack-bot"], - "report_emails": "urucrypto@gmail.com", - "smtp_usr": "SMTP USERNAME", - "smtp_from": "'From Name' ", - "smtp_key": "key" -} -``` -## Run -``` -$ npm run all -``` - -This will run the process in the foreground which is not recommended. We recommend using a tool such as [PM2](http://pm2.keymetrics.io/) to run the process in the background as well as providing many other great features. - -### To run each service apart - -``` -$ npm run api -$ npm run import -$ npm run curate -``` \ No newline at end of file +The work in this bot has been implemented so far by @cryptouru, based on a fork of yabapmatt's community bot https://github.com/MattyIce/communitybot From e5fc0f1d015e709c91cccaac7f26ee888e781e24 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Sun, 22 Jul 2018 15:53:55 -0300 Subject: [PATCH 012/193] added Dsteem & eslint - initial delegation retrival --- .eslintrc.js | 3 + delegations.js | 33 ++ package-lock.json | 1445 ++++++++++++++++++++++++++++++++++++++++++++- package.json | 7 + 4 files changed, 1485 insertions(+), 3 deletions(-) create mode 100644 .eslintrc.js create mode 100644 delegations.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..a5b82de --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + "extends": "standard" +}; \ No newline at end of file diff --git a/delegations.js b/delegations.js new file mode 100644 index 0000000..b2a2ad7 --- /dev/null +++ b/delegations.js @@ -0,0 +1,33 @@ +const dsteem = require('dsteem') +const client = new dsteem.Client('https://api.steemit.com') +const utils = require('./utils') + +var config = utils.getConfig() + +let delegationTransactions = [] +getDelegations(config.account, -1) + +async function getDelegations (account, start) { + let lastTrans = start + let limit = (start < 0) ? 3000 : Math.min(start, 3000) + console.log('Account: ' + account + ' - Start: ' + start + ' - Limit: ' + limit) + try { + const transactions = await client.database.call('get_account_history', [account, start, limit]) + transactions.reverse() + for (let txs of transactions) { + let op = txs[1].op + lastTrans = txs[0] + // console.log(txs) + if (op[0] === 'delegate_vesting_shares' && op[1].delegatee === account) { + console.log(op) + delegationTransactions.push({ id: txs[0], data: op[1], timestamp: txs[1].timestamp }) + } + } + if (start !== 0) getDelegations(account, lastTrans) + else console.log(delegationTransactions) + // console.log(transactions) + } catch (err) { + console.log(err) + if (err.type === 'request-timeout') getDelegations(account, start) + } +} diff --git a/package-lock.json b/package-lock.json index 6647fbb..0d1c42a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,21 @@ "negotiator": "0.6.1" } }, + "acorn": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", + "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", + "dev": true + }, + "acorn-jsx": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", + "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", + "dev": true, + "requires": { + "acorn": "^5.0.3" + } + }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -30,6 +45,12 @@ "json-schema-traverse": "^0.3.0" } }, + "ajv-keywords": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", + "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", + "dev": true + }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -54,6 +75,12 @@ "string-width": "^2.0.0" } }, + "ansi-escapes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "dev": true + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -79,6 +106,15 @@ "normalize-path": "^2.1.1" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -102,12 +138,33 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -175,6 +232,59 @@ "is-buffer": "^1.1.5" } }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -274,11 +384,29 @@ "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true }, + "bindings": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", + "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" + }, + "bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "bluebird": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, "body-parser": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", @@ -357,6 +485,11 @@ } } }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, "browserify-aes": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", @@ -397,6 +530,12 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, "bytebuffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", @@ -427,6 +566,21 @@ "unset-value": "^1.0.0" } }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -465,6 +619,12 @@ "supports-color": "^5.3.0" } }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, "chokidar": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", @@ -501,6 +661,12 @@ "safe-buffer": "^5.0.1" } }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -530,6 +696,21 @@ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", @@ -612,6 +793,12 @@ "xdg-basedir": "^3.0.0" } }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -638,6 +825,11 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, + "core-js": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -744,6 +936,12 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, "define-properties": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", @@ -800,6 +998,29 @@ } } }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -820,6 +1041,15 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=" }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", @@ -829,6 +1059,37 @@ "is-obj": "^1.0.0" } }, + "drbg.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", + "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", + "requires": { + "browserify-aes": "^1.0.6", + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4" + } + }, + "dsteem": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/dsteem/-/dsteem-0.9.0.tgz", + "integrity": "sha512-00t8Bekwfd4459Pjn0fcnb1LY8IT7Q+WGM0kJsI+CKWqOXP4xH/YuXwk7IPhDIo7la7Wskav6WmeVxFEutxq+w==", + "requires": { + "bs58": "^4.0.1", + "bytebuffer": "^5.0.1", + "core-js": "^2.5.0", + "node-fetch": "^2.1.2", + "secp256k1": "^3.3.1", + "verror": "^1.10.0", + "whatwg-fetch": "^2.0.3" + }, + "dependencies": { + "node-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", + "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" + } + } + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -864,6 +1125,20 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -877,6 +1152,39 @@ "iconv-lite": "~0.4.13" } }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", + "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" + } + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -888,6 +1196,283 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "eslint": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.2.0.tgz", + "integrity": "sha512-zlggW1qp7/TBjwLfouRoY7eWXrXwJZFqCdIxxh0/LVB/QuuKuIMkzyUZEcDo6LBadsry5JcEMxIqd3H/66CXVg==", + "dev": true, + "requires": { + "ajv": "^6.5.0", + "babel-code-frame": "^6.26.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^3.1.0", + "doctrine": "^2.1.0", + "eslint-scope": "^4.0.0", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^4.0.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^2.0.0", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.2", + "imurmurhash": "^0.1.4", + "inquirer": "^5.2.0", + "is-resolvable": "^1.1.0", + "js-yaml": "^3.11.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.5", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "pluralize": "^7.0.0", + "progress": "^2.0.0", + "regexpp": "^1.1.0", + "require-uncached": "^1.0.3", + "semver": "^5.5.0", + "string.prototype.matchall": "^2.0.0", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^4.0.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "ajv": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", + "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, + "eslint-config-standard": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-11.0.0.tgz", + "integrity": "sha512-oDdENzpViEe5fwuRCWla7AXQd++/oyIp8zP+iP9jiUPG6NBj3SHgdgtl/kTn00AjeN+1HNvavTKmYbMo+xMOlw==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.0" + } + }, + "eslint-module-utils": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz", + "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^1.0.0" + } + }, + "eslint-plugin-es": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.3.1.tgz", + "integrity": "sha512-9XcVyZiQRVeFjqHw8qHNDAZcQLqaHlOGGpeYqzYh8S4JYCWTCO3yzyen8yVmA5PratfzTRWDwCOFphtDEG+w/w==", + "dev": true, + "requires": { + "eslint-utils": "^1.3.0", + "regexpp": "^2.0.0" + }, + "dependencies": { + "regexpp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", + "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz", + "integrity": "sha512-t6hGKQDMIt9N8R7vLepsYXgDfeuhp6ZJSgtrLEDxonpSubyxUZHjhm6LsAaZX8q6GYVxkbT3kTsV9G5mBCFR6A==", + "dev": true, + "requires": { + "contains-path": "^0.1.0", + "debug": "^2.6.8", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.1", + "eslint-module-utils": "^2.2.0", + "has": "^1.0.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.3", + "read-pkg-up": "^2.0.0", + "resolve": "^1.6.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", + "integrity": "sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw==", + "dev": true, + "requires": { + "eslint-plugin-es": "^1.3.1", + "eslint-utils": "^1.3.1", + "ignore": "^4.0.2", + "minimatch": "^3.0.4", + "resolve": "^1.8.1", + "semver": "^5.5.0" + } + }, + "eslint-plugin-promise": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz", + "integrity": "sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==", + "dev": true + }, + "eslint-plugin-standard": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", + "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", + "dev": true + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", + "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", + "dev": true, + "requires": { + "acorn": "^5.6.0", + "acorn-jsx": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1042,6 +1627,17 @@ } } }, + "external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -1128,6 +1724,31 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" + } + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1165,6 +1786,28 @@ "unpipe": "~1.0.0" } }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "^0.3.1", + "del": "^2.0.2", + "graceful-fs": "^4.1.2", + "write": "^0.2.1" + } + }, "follow-redirects": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.0.tgz", @@ -1234,6 +1877,12 @@ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, "fsevents": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", @@ -1768,6 +2417,12 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -1830,6 +2485,48 @@ "ini": "^1.3.4" } }, + "globals": { + "version": "11.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", + "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "dev": true + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "got": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", @@ -1879,6 +2576,32 @@ "har-schema": "^2.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -1930,6 +2653,31 @@ "inherits": "^2.0.1" } }, + "hash.js": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", + "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -1961,6 +2709,12 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, + "ignore": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.2.tgz", + "integrity": "sha512-uoxnT7PYpyEnsja+yX+7v49B7LXxmzDJ2JALqHH3oEGzpM2U1IGcbfnOr8Dt57z3B/UWs7/iAgPFbmye8m4I0g==", + "dev": true + }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -1999,6 +2753,27 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, + "inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, "ipaddr.js": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", @@ -2013,6 +2788,12 @@ "kind-of": "^3.0.2" } }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", @@ -2027,6 +2808,21 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, "is-ci": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", @@ -2045,6 +2841,12 @@ "kind-of": "^3.0.2" } }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -2122,6 +2924,21 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, "is-path-inside": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", @@ -2140,12 +2957,33 @@ "isobject": "^3.0.1" } }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, "is-redirect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", "dev": true }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, "is-retry-allowed": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", @@ -2157,6 +2995,12 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -2189,6 +3033,22 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -2205,6 +3065,12 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -2244,6 +3110,54 @@ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "optional": true }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", @@ -2392,6 +3306,22 @@ "mime-db": "~1.33.0" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -2426,6 +3356,23 @@ } } }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, "moment": { "version": "2.22.2", "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", @@ -2453,12 +3400,16 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", - "dev": true, - "optional": true + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" }, "nanomatch": { "version": "1.2.13", @@ -2493,11 +3444,23 @@ } } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "nice-try": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", + "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "dev": true + }, "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", @@ -2558,6 +3521,18 @@ "abbrev": "1" } }, + "normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", @@ -2581,6 +3556,12 @@ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, "object-copy": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", @@ -2653,6 +3634,15 @@ "wrappy": "1" } }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -2662,6 +3652,34 @@ "wordwrap": "~0.0.2" } }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -2673,6 +3691,30 @@ "resolved": "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.7.tgz", "integrity": "sha512-VsYvUPjm2edbKkX4QzlASC1qB2e4Z6IE9WPaRVHKwCtobmB6vfUcU9eBOwj1k5uMNi8O6w89QfsDatO5ePSjQg==" }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, "package-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", @@ -2685,6 +3727,15 @@ "semver": "^5.1.0" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, "parseurl": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", @@ -2702,6 +3753,15 @@ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2719,11 +3779,34 @@ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -2744,6 +3827,36 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "^1.0.0" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -2755,6 +3868,12 @@ "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.1.tgz", "integrity": "sha1-uRepB5smF42aJK9aXNjLSpkdEbk=" }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", @@ -2767,6 +3886,12 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -2876,6 +4001,38 @@ } } }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + } + } + }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -2913,6 +4070,21 @@ "safe-regex": "^1.1.0" } }, + "regexp.prototype.flags": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", + "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2" + } + }, + "regexpp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true + }, "registry-auth-token": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", @@ -2976,6 +4148,24 @@ "uuid": "^3.1.0" } }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + } + } + }, "require_optional": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", @@ -2985,6 +4175,15 @@ "semver": "^5.1.0" } }, + "resolve": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", + "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", + "dev": true, + "requires": { + "path-parse": "^1.0.5" + } + }, "resolve-from": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", @@ -2996,6 +4195,16 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -3011,6 +4220,31 @@ "align-text": "^0.1.1" } }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "^7.0.5" + }, + "dependencies": { + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, "ripemd160": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", @@ -3020,6 +4254,24 @@ "inherits": "^2.0.1" } }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "5.5.11", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", + "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -3039,6 +4291,21 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "secp256k1": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.0.tgz", + "integrity": "sha512-e5QIJl8W7Y4tT6LHffVcZAxJjvpgE5Owawv6/XCYPQljE9aP2NFFddQ8OYMKhdLshNu88FfL3qCN3/xYkXGRsA==", + "requires": { + "bindings": "^1.2.1", + "bip66": "^1.1.3", + "bn.js": "^4.11.3", + "create-hash": "^1.1.2", + "drbg.js": "^1.0.1", + "elliptic": "^6.2.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, "secure-random": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/secure-random/-/secure-random-1.1.1.tgz", @@ -3151,6 +4418,15 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -3286,6 +4562,38 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, + "spdx-correct": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", + "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", + "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", + "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "dev": true + }, "split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", @@ -3304,6 +4612,12 @@ "extend-shallow": "^3.0.0" } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, "sshpk": { "version": "1.14.2", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", @@ -3389,6 +4703,19 @@ "strip-ansi": "^4.0.0" } }, + "string.prototype.matchall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", + "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.10.0", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "regexp.prototype.flags": "^1.2.0" + } + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3407,6 +4734,12 @@ "ansi-regex": "^3.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -3428,6 +4761,52 @@ "has-flag": "^3.0.0" } }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, + "table": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "dev": true, + "requires": { + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", + "chalk": "^2.1.0", + "lodash": "^4.17.4", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", + "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.1" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + } + } + }, "term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -3437,6 +4816,12 @@ "execa": "^0.7.0" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -3449,6 +4834,15 @@ "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", "dev": true }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -3511,6 +4905,15 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -3678,6 +5081,23 @@ "xdg-basedir": "^3.0.0" } }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + } + } + }, "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", @@ -3726,6 +5146,16 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" }, + "validate-npm-package-license": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", + "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3779,6 +5209,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "write-file-atomic": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", diff --git a/package.json b/package.json index b525543..bedb2b4 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "curation-bot.js", "dependencies": { "axios": "^0.18.0", + "dsteem": "^0.9.0", "express": "^4.16.2", "lodash": "^4.17.10", "moment": "^2.22.2", @@ -16,6 +17,12 @@ "steem": "^0.6.7" }, "devDependencies": { + "eslint": "^5.2.0", + "eslint-config-standard": "^11.0.0", + "eslint-plugin-import": "^2.13.0", + "eslint-plugin-node": "^7.0.1", + "eslint-plugin-promise": "^3.8.0", + "eslint-plugin-standard": "^3.1.0", "nodemon": "^1.17.5" }, "scripts": { From 7a7c9281e92bb63592f2fead15b42681f7cb5383 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Sun, 22 Jul 2018 20:17:13 -0300 Subject: [PATCH 013/193] saving of delegation transactions and active_delegations --- delegations.js | 108 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 10 deletions(-) diff --git a/delegations.js b/delegations.js index b2a2ad7..b626fe7 100644 --- a/delegations.js +++ b/delegations.js @@ -1,33 +1,121 @@ const dsteem = require('dsteem') const client = new dsteem.Client('https://api.steemit.com') const utils = require('./utils') +const mail = require('./mail') -var config = utils.getConfig() +const config = utils.getConfig() + +const MongoClient = require('mongodb').MongoClient + +let db +let collection +// Database Name +const dbName = config.db_name +const collectionName = 'delegation_transactions' + +let properties +let totalVests +let totalSteem + +// Use connect method to connect to the server +MongoClient.connect(config.mongo_uri, async function (err, dbClient) { + if (!err) { + console.log('Connected successfully to server: ' + config.mongo_uri) + + db = dbClient.db(dbName) + // Get the documents collection + collection = db.collection(collectionName) + startProcess() + } else { + utils.log(err, 'delegations') + mail.sendPlainMail('Database Error', err, config.report_emails) + .then(function (res, err) { + if (!err) { + console.log(res) + } else { + utils.log(err, 'import') + } + }) + process.exit() + } +}) let delegationTransactions = [] -getDelegations(config.account, -1) -async function getDelegations (account, start) { +async function startProcess () { + let end = 0 + // Find last saved delegation transaction + let lastTx = await collection.find().sort({'tx_date': -1}).limit(1).next() + console.log(lastTx) + if (lastTx) end = lastTx.tx_number + // Set STEEM global properties + properties = await client.database.getDynamicGlobalProperties() + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) + totalVests = Number(properties.total_vesting_shares.split(' ')[0]) + getDelegations(config.account, -1, end) +} + +async function getDelegations (account, start, end) { let lastTrans = start + let ended = false let limit = (start < 0) ? 3000 : Math.min(start, 3000) - console.log('Account: ' + account + ' - Start: ' + start + ' - Limit: ' + limit) + console.log('Account: ' + account + ' - Start: ' + start + ' - Limit: ' + limit + ' - Last Txs: ' + end) try { + // Query account history for delegations const transactions = await client.database.call('get_account_history', [account, start, limit]) transactions.reverse() for (let txs of transactions) { + if (txs[0] === end) { + console.log('--- Found last transaction ---') + ended = true + break + } let op = txs[1].op lastTrans = txs[0] - // console.log(txs) + // Look for delegation operations if (op[0] === 'delegate_vesting_shares' && op[1].delegatee === account) { - console.log(op) - delegationTransactions.push({ id: txs[0], data: op[1], timestamp: txs[1].timestamp }) + // Calculate in steem power + const delegatedVests = Number(op[1].vesting_shares.split(' ')[0]) + const steemPower = totalSteem * (delegatedVests / totalVests) + let data = op[1] + data.steem_power = +steemPower.toFixed(3) + data.tx_number = txs[0] + data.tx_date = new Date(txs[1].timestamp) + delegationTransactions.push(data) } } - if (start !== 0) getDelegations(account, lastTrans) - else console.log(delegationTransactions) + if (start !== limit && !ended) getDelegations(account, lastTrans, end) + else if (delegationTransactions.length > 0) { + // Insert new transactions and update active ones + console.log(delegationTransactions) + await collection.insert(delegationTransactions) + updateActiveDelegations() + } else console.log('--- No new delegations ---') // console.log(transactions) } catch (err) { console.log(err) - if (err.type === 'request-timeout') getDelegations(account, start) + if (err.type === 'request-timeout' || err.type === 'body-timeout') getDelegations(account, start, end) } } + +async function updateActiveDelegations () { + console.log('--- Updating active delegations ---') + let query = collection.aggregate( + [ + { $sort: { delegator: 1, tx_date: 1 } }, + { + $group: + { + _id: '$delegator', + steem_power: { $last: '$steem_power' }, + vests: { $last: '$vesting_shares' }, + tx_date: { $last: '$tx_date' } + } + }, + { $match: { 'steem_power': { '$gt': 0 } } } + ] + ) + let activeDelegations = await query.toArray() + await db.collection('active_delegations').drop() + return db.collection('active_delegations').insert(activeDelegations) +} From bca2da776248c9d2630d65cfcc66d3a3c94b8173 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Tue, 24 Jul 2018 19:27:22 -0300 Subject: [PATCH 014/193] fixed pagination conditional --- save-data.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/save-data.js b/save-data.js index 34f766e..96b98dc 100644 --- a/save-data.js +++ b/save-data.js @@ -109,10 +109,10 @@ function getPosts(index) { let last_post = posts[posts.length - 1]; await processTransactions(posts); console.log('Inserted transactions'); + postsProcessing = false; if (!index || (index.start_permlink != last_post.permlink && index.start_author != last_post.author && result.length >= 100)) return getPosts({start_author: last_post.author, start_permlink: last_post.permlink}); console.log('No more new posts'); - postsProcessing = false; return; }) .catch(function (err) { @@ -184,7 +184,7 @@ async function processTransactions(posts) { }); let reblogs = await steem.api.getRebloggedByAsync(post.author, post.permlink); console.log('------------------ REBLOGS --------------------'); - console.log(reblogs); + // console.log(reblogs); reblogs.forEach(async reblog => { if(reblog != post.author){ let reblog_transaction = { @@ -207,7 +207,7 @@ async function processTransactions(posts) { } }); }); - console.log(transactions); + // console.log(transactions); return bulk.execute(); } From 1673074b03e35ee7c5d1290ba68d479b1ee2e075 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Tue, 24 Jul 2018 23:04:51 -0300 Subject: [PATCH 015/193] process delegations --- delegations.js | 71 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/delegations.js b/delegations.js index b626fe7..e75a372 100644 --- a/delegations.js +++ b/delegations.js @@ -1,5 +1,7 @@ const dsteem = require('dsteem') const client = new dsteem.Client('https://api.steemit.com') +const _ = require('lodash') +const moment = require('moment') const utils = require('./utils') const mail = require('./mail') @@ -25,7 +27,8 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { db = dbClient.db(dbName) // Get the documents collection collection = db.collection(collectionName) - startProcess() + // startProcess() + processRewards() } else { utils.log(err, 'delegations') mail.sendPlainMail('Database Error', err, config.report_emails) @@ -94,6 +97,7 @@ async function getDelegations (account, start, end) { // console.log(transactions) } catch (err) { console.log(err) + // Consider exponential backoff if extreme cases start happening if (err.type === 'request-timeout' || err.type === 'body-timeout') getDelegations(account, start, end) } } @@ -119,3 +123,68 @@ async function updateActiveDelegations () { await db.collection('active_delegations').drop() return db.collection('active_delegations').insert(activeDelegations) } + +async function processRewards () { + let rewards = [] + let date = new Date() + let note = 'Delegation Reward Until EOD ' + date + // Get delegators who did not change their amount on the week + let fullWeekers = await db.collection('active_delegations').find( + {'tx_date': {$lte: new Date('2018-07-09')}}).toArray() + rewards = _.map(fullWeekers, function (o) { + return { + user: o._id, + token_count: +(o.steem_power * 7).toFixed(3), + reward_activity: 'Delegation', + note: note + } + }) + + // console.log(fullWeekers) + // Get transactions of the processed week + let weekTxs = await db.collection('delegation_transactions').find( + {'tx_date': {$gt: new Date('2018-07-09'), $lte: new Date('2018-07-16')}}).sort({tx_date: 1}).toArray() + + // console.log(weekTxs) + let groupedTxs = _.groupBy(weekTxs, 'delegator') + // console.log(groupedTxs) + for (let index in groupedTxs) { + if (groupedTxs[index].length > 1) { + let totalPower = 0 + let user = index + console.log('Multi week txs') + for (let i = 0; i < groupedTxs[index].length; i++) { + let txs = groupedTxs[index][i] + console.log(txs) + + let start = moment(txs.tx_date) + let end + if (i !== groupedTxs[index].length - 1) end = moment(groupedTxs[index][i + 1].tx_date) + else end = moment('2018-07-16') + let activeHours = end.diff(start, 'hours') + console.log(activeHours) + + let newPower = activeHours * (txs.steem_power / 24) + totalPower = totalPower + newPower + console.log(totalPower) + } + totalPower = +totalPower.toFixed(3) + rewards.push({user: user, token_count: totalPower, reward_activity: 'Delegation', date: date, note: note}) + } else { + let txs = groupedTxs[index][0] + console.log('Just one weekly txs') + console.log(txs) + let start = moment(txs.tx_date) + let end = moment('2018-07-16') + let activeHours = end.diff(start, 'hours') + console.log(activeHours) + let totalPower = activeHours * (txs.steem_power / 24) + totalPower = +totalPower.toFixed(3) + console.log(totalPower) + rewards.push({user: txs.delegator, token_count: totalPower, reward_activity: 'Delegation', date: date, note: note}) + } + } + console.log('--- REWARDS ---') + rewards = _.orderBy(rewards, ['user', 'token_count'], ['asc', 'desc']) + console.log(rewards) +} From 98145ea2be6031442c75bc5ba933af24dc7a57ae Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Wed, 25 Jul 2018 19:46:21 -0300 Subject: [PATCH 016/193] process token rewards to database --- delegations.js | 167 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 108 insertions(+), 59 deletions(-) diff --git a/delegations.js b/delegations.js index e75a372..0bfe896 100644 --- a/delegations.js +++ b/delegations.js @@ -28,7 +28,9 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { // Get the documents collection collection = db.collection(collectionName) // startProcess() - processRewards() + // processTokenRewards() + processBenefactorRewards('2016-01-01') + // getBenefactorRewards('actifit.pay') } else { utils.log(err, 'delegations') mail.sendPlainMail('Database Error', err, config.report_emails) @@ -78,8 +80,7 @@ async function getDelegations (account, start, end) { // Look for delegation operations if (op[0] === 'delegate_vesting_shares' && op[1].delegatee === account) { // Calculate in steem power - const delegatedVests = Number(op[1].vesting_shares.split(' ')[0]) - const steemPower = totalSteem * (delegatedVests / totalVests) + const steemPower = vestsToSteemPower(op[1].vesting_shares) let data = op[1] data.steem_power = +steemPower.toFixed(3) data.tx_number = txs[0] @@ -124,67 +125,115 @@ async function updateActiveDelegations () { return db.collection('active_delegations').insert(activeDelegations) } -async function processRewards () { - let rewards = [] - let date = new Date() - let note = 'Delegation Reward Until EOD ' + date - // Get delegators who did not change their amount on the week - let fullWeekers = await db.collection('active_delegations').find( - {'tx_date': {$lte: new Date('2018-07-09')}}).toArray() - rewards = _.map(fullWeekers, function (o) { - return { - user: o._id, - token_count: +(o.steem_power * 7).toFixed(3), - reward_activity: 'Delegation', - note: note - } - }) - - // console.log(fullWeekers) +async function processTokenRewards () { + let start = new Date('2018-07-09') + let end = new Date('2018-07-16') + let note = 'Delegation Reward Until EOD ' + moment(end).format('MMMM Do YYYY') + // Get active delegations for the week + let activeDelegations = await getActiveDelegations(start) // Get transactions of the processed week let weekTxs = await db.collection('delegation_transactions').find( {'tx_date': {$gt: new Date('2018-07-09'), $lte: new Date('2018-07-16')}}).sort({tx_date: 1}).toArray() - - // console.log(weekTxs) - let groupedTxs = _.groupBy(weekTxs, 'delegator') - // console.log(groupedTxs) + let allTxs = activeDelegations.concat(weekTxs) + let groupedTxs = _.groupBy(allTxs, 'delegator') for (let index in groupedTxs) { - if (groupedTxs[index].length > 1) { - let totalPower = 0 - let user = index - console.log('Multi week txs') - for (let i = 0; i < groupedTxs[index].length; i++) { - let txs = groupedTxs[index][i] - console.log(txs) - - let start = moment(txs.tx_date) - let end - if (i !== groupedTxs[index].length - 1) end = moment(groupedTxs[index][i + 1].tx_date) - else end = moment('2018-07-16') - let activeHours = end.diff(start, 'hours') - console.log(activeHours) - - let newPower = activeHours * (txs.steem_power / 24) - totalPower = totalPower + newPower - console.log(totalPower) - } - totalPower = +totalPower.toFixed(3) - rewards.push({user: user, token_count: totalPower, reward_activity: 'Delegation', date: date, note: note}) - } else { - let txs = groupedTxs[index][0] - console.log('Just one weekly txs') - console.log(txs) - let start = moment(txs.tx_date) - let end = moment('2018-07-16') - let activeHours = end.diff(start, 'hours') - console.log(activeHours) - let totalPower = activeHours * (txs.steem_power / 24) - totalPower = +totalPower.toFixed(3) - console.log(totalPower) - rewards.push({user: txs.delegator, token_count: totalPower, reward_activity: 'Delegation', date: date, note: note}) + let totalPower = 0 + let user = index + if (index === 'nataboo') console.log(groupedTxs[index]) + for (let i = 0; i < groupedTxs[index].length; i++) { + let txs = groupedTxs[index][i] + let endTxs + if (i !== groupedTxs[index].length - 1) endTxs = new Date(groupedTxs[index][i + 1].tx_date) + else endTxs = end + var activeHours = Math.abs(txs.tx_date - endTxs) / 36e5 + let newPower = activeHours * (txs.steem_power / 24) + totalPower = totalPower + newPower + } + totalPower = +totalPower.toFixed(3) + let reward = { + user: user, + token_count: totalPower, + reward_activity: 'Delegation', + note: note, + date: end } + upsertRewardTransaction(reward) } - console.log('--- REWARDS ---') - rewards = _.orderBy(rewards, ['user', 'token_count'], ['asc', 'desc']) +} + +function upsertRewardTransaction (reward) { + return db.collection('token_transactions').update( + { user: reward.user, date: reward.date, reward_activity: reward.reward_activity }, + reward, + { upsert: true } + ) +} + +async function processBenefactorRewards (start) { + // Get active delegations for the week + let activeDelegations = await getActiveDelegations(start) + let rewards = await getBenefactorRewards(start) console.log(rewards) } + +async function getBenefactorRewards (delDate) { + let totalSp = 0 + let start = moment(delDate).subtract(14, 'days').calendar() + let end = moment(delDate).subtract(7, 'days').calendar() + // Query account history for delegations + properties = await client.database.getDynamicGlobalProperties() + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) + totalVests = Number(properties.total_vesting_shares.split(' ')[0]) + const transactions = await client.database.call('get_account_history', [config.account, -1, 3000]) + transactions.reverse() + for (let txs of transactions) { + let date = new Date(txs[1].timestamp) + if (date >= start && date <= end) { + let op = txs[1].op + console.log(date) + // Look for delegation operations + if (op[0] === 'comment_benefactor_reward') { + console.log(op) + let newSp = vestsToSteemPower(op[1].reward) + console.log(newSp) + totalSp = totalSp + newSp + } + } else if (date < start) break + } + console.log('-- Processed rewards ---') + console.log(totalSp.toFixed(3)) + return totalSp +} + +async function getActiveDelegations (start) { + return collection.aggregate( + [ + { $match: { 'tx_date': { '$lte': start } } }, + { $sort: { delegator: 1, tx_date: 1 } }, + { + $group: + { + _id: '$delegator', + steem_power: { $last: '$steem_power' }, + vests: { $last: '$vesting_shares' }, + tx_date: { $last: '$tx_date' } + } + }, + { $project: + { + _id: '$_id', + delegator: '$_id', + steem_power: 1, + tx_date: start + } + }, + { $match: { 'steem_power': { '$gt': 0 } } }, + { $sort: { tx_date: 1 } } + ] + ).toArray() +} + +function vestsToSteemPower (vests) { + vests = Number(vests.split(' ')[0]) + return (totalSteem * (vests / totalVests)) +} From cbe18e95b1bbbeff8028cb1442e7719ffcdef8e3 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Sun, 29 Jul 2018 13:04:44 -0300 Subject: [PATCH 017/193] delegation rewards math --- delegations.js | 58 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/delegations.js b/delegations.js index 0bfe896..3570caa 100644 --- a/delegations.js +++ b/delegations.js @@ -29,7 +29,7 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { collection = db.collection(collectionName) // startProcess() // processTokenRewards() - processBenefactorRewards('2016-01-01') + processBenefactorRewards('2018-07-23') // getBenefactorRewards('actifit.pay') } else { utils.log(err, 'delegations') @@ -133,16 +133,18 @@ async function processTokenRewards () { let activeDelegations = await getActiveDelegations(start) // Get transactions of the processed week let weekTxs = await db.collection('delegation_transactions').find( - {'tx_date': {$gt: new Date('2018-07-09'), $lte: new Date('2018-07-16')}}).sort({tx_date: 1}).toArray() + {'tx_date': {$gt: new Date('2018-07-09'), $lte: new Date('2018-07-16')}, + 'delegator': {$nin: config.exclude_rewards}}) + .sort({tx_date: 1}).toArray() let allTxs = activeDelegations.concat(weekTxs) let groupedTxs = _.groupBy(allTxs, 'delegator') for (let index in groupedTxs) { let totalPower = 0 let user = index - if (index === 'nataboo') console.log(groupedTxs[index]) for (let i = 0; i < groupedTxs[index].length; i++) { let txs = groupedTxs[index][i] let endTxs + // If not last transaction calculate up to next one if (i !== groupedTxs[index].length - 1) endTxs = new Date(groupedTxs[index][i + 1].tx_date) else endTxs = end var activeHours = Math.abs(txs.tx_date - endTxs) / 36e5 @@ -171,41 +173,60 @@ function upsertRewardTransaction (reward) { async function processBenefactorRewards (start) { // Get active delegations for the week - let activeDelegations = await getActiveDelegations(start) - let rewards = await getBenefactorRewards(start) - console.log(rewards) + console.log(config.pay_account) + const delDate = moment(start).subtract(7, 'days').toDate() + const end = moment(start).add(7, 'days').format() + Promise.all([getActiveDelegations(delDate), getBenefactorRewards(delDate, end, -1)]).then(values => { + const activeDelegations = values[0] + const steemRewards = values[1] + const totalDelegatedSteem = _.sumBy(activeDelegations, 'steem_power') + const rewardPerSteem = steemRewards / totalDelegatedSteem + const rewards = _.map(activeDelegations, function (o) { + return { + delegator: o.delegator, + reward: o.steem_power * rewardPerSteem + } + }) + console.log(rewards) + }) } -async function getBenefactorRewards (delDate) { - let totalSp = 0 - let start = moment(delDate).subtract(14, 'days').calendar() - let end = moment(delDate).subtract(7, 'days').calendar() +async function getBenefactorRewards (start, end,txStart, totalSp) { + if (!totalSp) totalSp = 0 + let limit = (txStart < 0) ? 10000 : Math.min(txStart, 10000) + start = moment(start).format() + console.log(start) + console.log(end) // Query account history for delegations properties = await client.database.getDynamicGlobalProperties() totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) totalVests = Number(properties.total_vesting_shares.split(' ')[0]) - const transactions = await client.database.call('get_account_history', [config.account, -1, 3000]) + const transactions = await client.database.call('get_account_history', [config.pay_account, txStart, limit]) transactions.reverse() for (let txs of transactions) { - let date = new Date(txs[1].timestamp) + let date = moment(txs[1].timestamp).format() if (date >= start && date <= end) { let op = txs[1].op - console.log(date) // Look for delegation operations if (op[0] === 'comment_benefactor_reward') { - console.log(op) let newSp = vestsToSteemPower(op[1].reward) - console.log(newSp) totalSp = totalSp + newSp } } else if (date < start) break } + // Check last tx date to see if pagination is needed + let lastTx = transactions[transactions.length - 1] + let lastDate = moment(lastTx[1].timestamp).format() + console.log(lastDate) + if (lastDate >= start) return getBenefactorRewards(start, totalSp, lastTx[0]) + console.log('-- Processed rewards ---') console.log(totalSp.toFixed(3)) - return totalSp + return +totalSp.toFixed(3) } async function getActiveDelegations (start) { + start = new Date(start) return collection.aggregate( [ { $match: { 'tx_date': { '$lte': start } } }, @@ -227,7 +248,7 @@ async function getActiveDelegations (start) { tx_date: start } }, - { $match: { 'steem_power': { '$gt': 0 } } }, + { $match: { 'steem_power': { '$gt': 0 }, 'delegator': {$nin: config.exclude_rewards} } }, { $sort: { tx_date: 1 } } ] ).toArray() @@ -235,5 +256,6 @@ async function getActiveDelegations (start) { function vestsToSteemPower (vests) { vests = Number(vests.split(' ')[0]) - return (totalSteem * (vests / totalVests)) + const steemPower = (totalSteem * (vests / totalVests)) + return steemPower } From 47cff3a9e45d144de408dffbee07309a08a66bcc Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Sun, 29 Jul 2018 14:20:02 -0300 Subject: [PATCH 018/193] moved functions around added date option to functions --- delegations.js | 192 +++++++++++++++++++++++++------------------------ 1 file changed, 100 insertions(+), 92 deletions(-) diff --git a/delegations.js b/delegations.js index 3570caa..7570895 100644 --- a/delegations.js +++ b/delegations.js @@ -27,9 +27,9 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { db = dbClient.db(dbName) // Get the documents collection collection = db.collection(collectionName) - // startProcess() + startProcess() // processTokenRewards() - processBenefactorRewards('2018-07-23') + // processSteemRewards('2018-07-23') // getBenefactorRewards('actifit.pay') } else { utils.log(err, 'delegations') @@ -45,95 +45,26 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { } }) -let delegationTransactions = [] - async function startProcess () { let end = 0 // Find last saved delegation transaction let lastTx = await collection.find().sort({'tx_date': -1}).limit(1).next() console.log(lastTx) if (lastTx) end = lastTx.tx_number - // Set STEEM global properties - properties = await client.database.getDynamicGlobalProperties() - totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) - totalVests = Number(properties.total_vesting_shares.split(' ')[0]) - getDelegations(config.account, -1, end) + await updateProperties() + processDelegations(config.account, -1, end) + processTokenRewards() } -async function getDelegations (account, start, end) { - let lastTrans = start - let ended = false - let limit = (start < 0) ? 3000 : Math.min(start, 3000) - console.log('Account: ' + account + ' - Start: ' + start + ' - Limit: ' + limit + ' - Last Txs: ' + end) - try { - // Query account history for delegations - const transactions = await client.database.call('get_account_history', [account, start, limit]) - transactions.reverse() - for (let txs of transactions) { - if (txs[0] === end) { - console.log('--- Found last transaction ---') - ended = true - break - } - let op = txs[1].op - lastTrans = txs[0] - // Look for delegation operations - if (op[0] === 'delegate_vesting_shares' && op[1].delegatee === account) { - // Calculate in steem power - const steemPower = vestsToSteemPower(op[1].vesting_shares) - let data = op[1] - data.steem_power = +steemPower.toFixed(3) - data.tx_number = txs[0] - data.tx_date = new Date(txs[1].timestamp) - delegationTransactions.push(data) - } - } - if (start !== limit && !ended) getDelegations(account, lastTrans, end) - else if (delegationTransactions.length > 0) { - // Insert new transactions and update active ones - console.log(delegationTransactions) - await collection.insert(delegationTransactions) - updateActiveDelegations() - } else console.log('--- No new delegations ---') - // console.log(transactions) - } catch (err) { - console.log(err) - // Consider exponential backoff if extreme cases start happening - if (err.type === 'request-timeout' || err.type === 'body-timeout') getDelegations(account, start, end) - } -} - -async function updateActiveDelegations () { - console.log('--- Updating active delegations ---') - let query = collection.aggregate( - [ - { $sort: { delegator: 1, tx_date: 1 } }, - { - $group: - { - _id: '$delegator', - steem_power: { $last: '$steem_power' }, - vests: { $last: '$vesting_shares' }, - tx_date: { $last: '$tx_date' } - } - }, - { $match: { 'steem_power': { '$gt': 0 } } } - ] - ) - let activeDelegations = await query.toArray() - await db.collection('active_delegations').drop() - return db.collection('active_delegations').insert(activeDelegations) -} - -async function processTokenRewards () { - let start = new Date('2018-07-09') - let end = new Date('2018-07-16') +async function processTokenRewards (start, end) { + if (!start) start = moment().utc().startOf('date').subtract(1, 'days').toDate() + if (!end) end = moment().utc().startOf('date').toDate() let note = 'Delegation Reward Until EOD ' + moment(end).format('MMMM Do YYYY') // Get active delegations for the week let activeDelegations = await getActiveDelegations(start) // Get transactions of the processed week let weekTxs = await db.collection('delegation_transactions').find( - {'tx_date': {$gt: new Date('2018-07-09'), $lte: new Date('2018-07-16')}, + {'tx_date': {$gt: start, $lte: end}, 'delegator': {$nin: config.exclude_rewards}}) .sort({tx_date: 1}).toArray() let allTxs = activeDelegations.concat(weekTxs) @@ -159,24 +90,17 @@ async function processTokenRewards () { note: note, date: end } + console.log(reward) upsertRewardTransaction(reward) } } -function upsertRewardTransaction (reward) { - return db.collection('token_transactions').update( - { user: reward.user, date: reward.date, reward_activity: reward.reward_activity }, - reward, - { upsert: true } - ) -} - -async function processBenefactorRewards (start) { +async function processSteemRewards (start) { + if (!start) start = moment().utc().startOf('date').toDate() // Get active delegations for the week console.log(config.pay_account) const delDate = moment(start).subtract(7, 'days').toDate() - const end = moment(start).add(7, 'days').format() - Promise.all([getActiveDelegations(delDate), getBenefactorRewards(delDate, end, -1)]).then(values => { + Promise.all([getActiveDelegations(delDate), getBenefactorRewards(delDate, start, -1)]).then(values => { const activeDelegations = values[0] const steemRewards = values[1] const totalDelegatedSteem = _.sumBy(activeDelegations, 'steem_power') @@ -188,13 +112,60 @@ async function processBenefactorRewards (start) { } }) console.log(rewards) + console.log(steemRewards) }) } -async function getBenefactorRewards (start, end,txStart, totalSp) { +async function processDelegations (account, start, end) { + let delegationTransactions = [] + let lastTrans = start + let ended = false + let limit = (start < 0) ? 3000 : Math.min(start, 3000) + console.log('Account: ' + account + ' - Start: ' + start + ' - Limit: ' + limit + ' - Last Txs: ' + end) + try { + // Query account history for delegations + const transactions = await client.database.call('get_account_history', [account, start, limit]) + transactions.reverse() + for (let txs of transactions) { + if (txs[0] === end) { + console.log('--- Found last transaction ---') + ended = true + break + } + let op = txs[1].op + lastTrans = txs[0] + // Look for delegation operations + if (op[0] === 'delegate_vesting_shares' && op[1].delegatee === account) { + // Calculate in steem power + const steemPower = vestsToSteemPower(op[1].vesting_shares) + let data = op[1] + data.steem_power = +steemPower.toFixed(3) + data.tx_number = txs[0] + data.tx_date = new Date(txs[1].timestamp) + delegationTransactions.push(data) + } + } + // Insert new transactions and update active ones + // console.log(delegationTransactions) + if (delegationTransactions.length > 0) { + await collection.insert(delegationTransactions) + updateActiveDelegations() + } else console.log('--- No new delegations ---') + // If more pending delegations call process againg with new index + if (start !== limit && !ended) processDelegations(account, lastTrans, end) + // console.log(transactions) + } catch (err) { + console.log(err) + // Consider exponential backoff if extreme cases start happening + if (err.type === 'request-timeout' || err.type === 'body-timeout') processDelegations(account, start, end) + } +} + +async function getBenefactorRewards (start, end, txStart, totalSp) { if (!totalSp) totalSp = 0 let limit = (txStart < 0) ? 10000 : Math.min(txStart, 10000) start = moment(start).format() + end = moment(end).format() console.log(start) console.log(end) // Query account history for delegations @@ -217,11 +188,11 @@ async function getBenefactorRewards (start, end,txStart, totalSp) { // Check last tx date to see if pagination is needed let lastTx = transactions[transactions.length - 1] let lastDate = moment(lastTx[1].timestamp).format() - console.log(lastDate) + // console.log(lastDate) if (lastDate >= start) return getBenefactorRewards(start, totalSp, lastTx[0]) console.log('-- Processed rewards ---') - console.log(totalSp.toFixed(3)) + // console.log(totalSp.toFixed(3)) return +totalSp.toFixed(3) } @@ -254,8 +225,45 @@ async function getActiveDelegations (start) { ).toArray() } +async function updateActiveDelegations () { + console.log('--- Updating active delegations ---') + let query = collection.aggregate( + [ + { $sort: { delegator: 1, tx_date: 1 } }, + { + $group: + { + _id: '$delegator', + steem_power: { $last: '$steem_power' }, + vests: { $last: '$vesting_shares' }, + tx_date: { $last: '$tx_date' } + } + }, + { $match: { 'steem_power': { '$gt': 0 } } } + ] + ) + let activeDelegations = await query.toArray() + await db.collection('active_delegations').drop() + return db.collection('active_delegations').insert(activeDelegations) +} + +function upsertRewardTransaction (reward) { + return db.collection('token_transactions').update( + { user: reward.user, date: reward.date, reward_activity: reward.reward_activity }, + reward, + { upsert: true } + ) +} + function vestsToSteemPower (vests) { vests = Number(vests.split(' ')[0]) const steemPower = (totalSteem * (vests / totalVests)) return steemPower } + +async function updateProperties () { + // Set STEEM global properties + properties = await client.database.getDynamicGlobalProperties() + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) + totalVests = Number(properties.total_vesting_shares.split(' ')[0]) +} From 179a73fa976083340774540ae883caa07eb0c8b5 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Sun, 29 Jul 2018 16:37:15 -0300 Subject: [PATCH 019/193] added getAcumulatedRewards function and email template --- delegations.js | 82 +++++++++++--- mail.js | 2 +- views/rewards.handlebars | 237 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+), 15 deletions(-) create mode 100644 views/rewards.handlebars diff --git a/delegations.js b/delegations.js index 7570895..b9812a9 100644 --- a/delegations.js +++ b/delegations.js @@ -27,9 +27,11 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { db = dbClient.db(dbName) // Get the documents collection collection = db.collection(collectionName) - startProcess() + // startProcess() // processTokenRewards() - // processSteemRewards('2018-07-23') + processSteemRewards('2018-07-23') + // let rewards = await getAcumulatedRewards('2018-07-09', '2018-07-16') + // console.log(rewards) // getBenefactorRewards('actifit.pay') } else { utils.log(err, 'delegations') @@ -70,7 +72,7 @@ async function processTokenRewards (start, end) { let allTxs = activeDelegations.concat(weekTxs) let groupedTxs = _.groupBy(allTxs, 'delegator') for (let index in groupedTxs) { - let totalPower = 0 + let totalReward = 0 let user = index for (let i = 0; i < groupedTxs[index].length; i++) { let txs = groupedTxs[index][i] @@ -79,13 +81,13 @@ async function processTokenRewards (start, end) { if (i !== groupedTxs[index].length - 1) endTxs = new Date(groupedTxs[index][i + 1].tx_date) else endTxs = end var activeHours = Math.abs(txs.tx_date - endTxs) / 36e5 - let newPower = activeHours * (txs.steem_power / 24) - totalPower = totalPower + newPower + let newReward = activeHours * (txs.steem_power / 24) + totalReward = totalReward + newReward } - totalPower = +totalPower.toFixed(3) + totalReward = +totalReward.toFixed(3) let reward = { user: user, - token_count: totalPower, + token_count: totalReward, reward_activity: 'Delegation', note: note, date: end @@ -99,20 +101,33 @@ async function processSteemRewards (start) { if (!start) start = moment().utc().startOf('date').toDate() // Get active delegations for the week console.log(config.pay_account) - const delDate = moment(start).subtract(7, 'days').toDate() - Promise.all([getActiveDelegations(delDate), getBenefactorRewards(delDate, start, -1)]).then(values => { - const activeDelegations = values[0] + const to = moment(start).subtract(7, 'days').toDate() + const from = moment(to).subtract(7, 'days').toDate() + Promise.all([getAcumulatedRewards(from, to), getBenefactorRewards(to, start, -1)]).then(values => { + const activeDelegations = values[0].users const steemRewards = values[1] - const totalDelegatedSteem = _.sumBy(activeDelegations, 'steem_power') + const totalDelegatedSteem = values[0].totalSteem const rewardPerSteem = steemRewards / totalDelegatedSteem const rewards = _.map(activeDelegations, function (o) { - return { - delegator: o.delegator, - reward: o.steem_power * rewardPerSteem + let reward = { + user: o.user, + steem: +(o.totalSteem * rewardPerSteem).toFixed(3) } + let url = 'https://v2.steemconnect.com/sign/transfer?from=[PAY_ACCOUNT]&to=[TO_ACCOUNT]&amount=[AMOUNT]%20STEEM&memo=Delegation%20Rewards' + url = url.replace('[PAY_ACCOUNT]', config.pay_account) + url = url.replace('[TO_ACCOUNT]', reward.user) + url = url.replace('[AMOUNT]', reward.steem) + reward.url = url + return reward }) console.log(rewards) console.log(steemRewards) + const data = { + rewards: rewards, + total: steemRewards, + totalUsers: rewards.length + } + mail.sendWithTemplate('Rewards mail', data, 'cryptouru@gmail.com', 'rewards') }) } @@ -225,6 +240,45 @@ async function getActiveDelegations (start) { ).toArray() } +async function getAcumulatedRewards (from, to) { + let result = { + users: [] + } + let totalSteem = 0 + from = moment(from).toDate() + to = moment(to).toDate() + // Get active delegations for the week + let activeDelegations = await getActiveDelegations(from) + // Get transactions of the processed week + let weekTxs = await db.collection('delegation_transactions').find( + {'tx_date': {$gt: from, $lte: to}, + 'delegator': {$nin: config.exclude_rewards}}) + .sort({tx_date: 1}).toArray() + let allTxs = activeDelegations.concat(weekTxs) + let groupedTxs = _.groupBy(allTxs, 'delegator') + for (let index in groupedTxs) { + let totalReward = 0 + for (let i = 0; i < groupedTxs[index].length; i++) { + let txs = groupedTxs[index][i] + let endTxs + // If not last transaction calculate up to next one + if (i !== groupedTxs[index].length - 1) endTxs = new Date(groupedTxs[index][i + 1].tx_date) + else endTxs = to + var activeHours = Math.abs(txs.tx_date - endTxs) / 36e5 + let newReward = activeHours * (txs.steem_power / 24) + totalReward = totalReward + newReward + } + totalSteem = totalSteem + totalReward + let user = { + user: index, + totalSteem: totalReward + } + result.users.push(user) + } + result.totalSteem = totalSteem + return result +} + async function updateActiveDelegations () { console.log('--- Updating active delegations ---') let query = collection.aggregate( diff --git a/mail.js b/mail.js index cbfe2de..f74b1ad 100644 --- a/mail.js +++ b/mail.js @@ -74,7 +74,7 @@ function sendWithTemplate(subject, data, to, template) { transporter.sendMail(mailOptions, (error, info) => { if (error) { console.log(error); - return reject(errir); + return reject(error); } else { console.log('Message sent: %s', info.messageId); // Message sent: diff --git a/views/rewards.handlebars b/views/rewards.handlebars new file mode 100644 index 0000000..d9a4cff --- /dev/null +++ b/views/rewards.handlebars @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + +
+This week token rewards! +
+ + + + + +
+ + + + + + + + + + + +
+ +
+ + + + +
+

Actifit

+
+
+ +
+ + + + +
+ + + + +
+

{{curDate}}

+
+
+
+ +
+ + + + + + + + + + + + + + +
+

+ Weekly benefactor steem rewards! +

+
+

+ INFO: +

+
+ + + + + + + {{#each rewards}} + + + + + + {{/each}} +
+ Username + + Steem Reward + + URL +
+ {{user}} + + {{steem}} + + Pay now! +
+
+ + + + + + +
+ TOTAL + + Active delegations: {{totalUsers}} + + Benefactor rewards: {{total}} +
+
+ +
+ + + + + +
+ +
+ + + + + +
+

Courtesy of:

+

@cryptouru

+ +
+
+ + +
+ +
+ +
+ + + From 4e4d9a09534c4e80d3c2a77aec0da36a38c40b81 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Sun, 29 Jul 2018 18:57:04 -0300 Subject: [PATCH 020/193] send rewards json as attachement and update config example --- config-example.json | 2 ++ delegations.js | 48 ++++++++++++++++----------------------------- mail.js | 5 ++++- 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/config-example.json b/config-example.json index c1d9e34..588ccfc 100644 --- a/config-example.json +++ b/config-example.json @@ -4,6 +4,7 @@ "detailed_logging": false, "account": "actifit", "main_tag": "actifit", + "pay_account": "actifit.pay", "memo_key": "your_private_memo_key", "posting_key": "your_private_posting_key", "active_key": "your_private_active_key", @@ -14,6 +15,7 @@ "comment": false, "resteem": false, "no_paid_bots": true, + "exclude_rewards": ["mcfarhat"], "beneficiaries": ["actifit", "actifit.pay"], "flag_signal_accounts": ["spaminator", "cheetah", "steemcleaners", "mack-bot"], "mongo_uri": "mongodb://localhost:27017", diff --git a/delegations.js b/delegations.js index b9812a9..6a2156c 100644 --- a/delegations.js +++ b/delegations.js @@ -55,39 +55,21 @@ async function startProcess () { if (lastTx) end = lastTx.tx_number await updateProperties() processDelegations(config.account, -1, end) - processTokenRewards() + let start = moment('2018-07-16').utc().toDate() + let txEnd = moment('2018-07-23').utc().toDate() + processTokenRewards(start, txEnd) } async function processTokenRewards (start, end) { if (!start) start = moment().utc().startOf('date').subtract(1, 'days').toDate() if (!end) end = moment().utc().startOf('date').toDate() - let note = 'Delegation Reward Until EOD ' + moment(end).format('MMMM Do YYYY') - // Get active delegations for the week - let activeDelegations = await getActiveDelegations(start) - // Get transactions of the processed week - let weekTxs = await db.collection('delegation_transactions').find( - {'tx_date': {$gt: start, $lte: end}, - 'delegator': {$nin: config.exclude_rewards}}) - .sort({tx_date: 1}).toArray() - let allTxs = activeDelegations.concat(weekTxs) - let groupedTxs = _.groupBy(allTxs, 'delegator') - for (let index in groupedTxs) { - let totalReward = 0 - let user = index - for (let i = 0; i < groupedTxs[index].length; i++) { - let txs = groupedTxs[index][i] - let endTxs - // If not last transaction calculate up to next one - if (i !== groupedTxs[index].length - 1) endTxs = new Date(groupedTxs[index][i + 1].tx_date) - else endTxs = end - var activeHours = Math.abs(txs.tx_date - endTxs) / 36e5 - let newReward = activeHours * (txs.steem_power / 24) - totalReward = totalReward + newReward - } - totalReward = +totalReward.toFixed(3) + let note = 'Delegation Reward Until EOD ' + moment(end).subtract(1, 'days').format('MMMM Do YYYY') + let acumulatedSteemPower = await getAcumulatedSteemPower(start, end) + console.log(acumulatedSteemPower) + for (let user of acumulatedSteemPower.users) { let reward = { - user: user, - token_count: totalReward, + user: user.user, + token_count: user.totalSteem, reward_activity: 'Delegation', note: note, date: end @@ -103,7 +85,7 @@ async function processSteemRewards (start) { console.log(config.pay_account) const to = moment(start).subtract(7, 'days').toDate() const from = moment(to).subtract(7, 'days').toDate() - Promise.all([getAcumulatedRewards(from, to), getBenefactorRewards(to, start, -1)]).then(values => { + Promise.all([getAcumulatedSteemPower(from, to), getBenefactorRewards(to, start, -1)]).then(values => { const activeDelegations = values[0].users const steemRewards = values[1] const totalDelegatedSteem = values[0].totalSteem @@ -127,7 +109,11 @@ async function processSteemRewards (start) { total: steemRewards, totalUsers: rewards.length } - mail.sendWithTemplate('Rewards mail', data, 'cryptouru@gmail.com', 'rewards') + const attachment = { + filename: 'rewards.json', + content: JSON.stringify(rewards) + } + mail.sendWithTemplate('Rewards mail', data, config.report_emails, 'rewards', attachment) }) } @@ -240,7 +226,7 @@ async function getActiveDelegations (start) { ).toArray() } -async function getAcumulatedRewards (from, to) { +async function getAcumulatedSteemPower (from, to) { let result = { users: [] } @@ -251,7 +237,7 @@ async function getAcumulatedRewards (from, to) { let activeDelegations = await getActiveDelegations(from) // Get transactions of the processed week let weekTxs = await db.collection('delegation_transactions').find( - {'tx_date': {$gt: from, $lte: to}, + {'tx_date': {$gt: from, $lt: to}, 'delegator': {$nin: config.exclude_rewards}}) .sort({tx_date: 1}).toArray() let allTxs = activeDelegations.concat(weekTxs) diff --git a/mail.js b/mail.js index f74b1ad..22adbac 100644 --- a/mail.js +++ b/mail.js @@ -45,7 +45,7 @@ function sendPlainMail(subject, message, to) { } -function sendWithTemplate(subject, data, to, template) { +function sendWithTemplate(subject, data, to, template, attachment) { if(Array.isArray(to)) to = to.join(','); @@ -68,6 +68,9 @@ function sendWithTemplate(subject, data, to, template) { mailOptions.to = to; mailOptions.template = template; mailOptions.context = data; + if (attachment) { + mailOptions.attachments = [attachment] + } // send mail with defined transport object return new Promise((resolve, reject) => { From e63f5fa76fde45b8d416aa1021c6589f7b04f083 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Sun, 29 Jul 2018 22:48:35 -0300 Subject: [PATCH 021/193] ready process up --- delegations.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/delegations.js b/delegations.js index 6a2156c..fac20be 100644 --- a/delegations.js +++ b/delegations.js @@ -27,9 +27,9 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { db = dbClient.db(dbName) // Get the documents collection collection = db.collection(collectionName) - // startProcess() + startProcess() // processTokenRewards() - processSteemRewards('2018-07-23') + // processSteemRewards('2018-07-23') // let rewards = await getAcumulatedRewards('2018-07-09', '2018-07-16') // console.log(rewards) // getBenefactorRewards('actifit.pay') @@ -55,9 +55,10 @@ async function startProcess () { if (lastTx) end = lastTx.tx_number await updateProperties() processDelegations(config.account, -1, end) - let start = moment('2018-07-16').utc().toDate() - let txEnd = moment('2018-07-23').utc().toDate() + let start = moment().utc().startOf('date').subtract(7, 'days').toDate() + let txEnd = moment().utc().startOf('date').toDate() processTokenRewards(start, txEnd) + processSteemRewards(txEnd) } async function processTokenRewards (start, end) { From 88aad27f8d46f81f36ab97ddef63d7ddff24d853 Mon Sep 17 00:00:00 2001 From: Cryptouru Date: Sun, 5 Aug 2018 18:12:57 -0300 Subject: [PATCH 022/193] scheduled process & SP multiplier --- config-example.json | 1 + delegations.js | 28 +++++++++++++++++++--------- package-lock.json | 45 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/config-example.json b/config-example.json index 588ccfc..ce2c08b 100644 --- a/config-example.json +++ b/config-example.json @@ -16,6 +16,7 @@ "resteem": false, "no_paid_bots": true, "exclude_rewards": ["mcfarhat"], + "weekly_rewards_limit": 700000, "beneficiaries": ["actifit", "actifit.pay"], "flag_signal_accounts": ["spaminator", "cheetah", "steemcleaners", "mack-bot"], "mongo_uri": "mongodb://localhost:27017", diff --git a/delegations.js b/delegations.js index fac20be..c1437a6 100644 --- a/delegations.js +++ b/delegations.js @@ -2,6 +2,7 @@ const dsteem = require('dsteem') const client = new dsteem.Client('https://api.steemit.com') const _ = require('lodash') const moment = require('moment') +var schedule = require('node-schedule') const utils = require('./utils') const mail = require('./mail') @@ -27,7 +28,9 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { db = dbClient.db(dbName) // Get the documents collection collection = db.collection(collectionName) - startProcess() + //startProcess() + // schedule to run every monday at 00:00 + let jobd = schedule.scheduleJob('0 0 * * 1', startProcess) // processTokenRewards() // processSteemRewards('2018-07-23') // let rewards = await getAcumulatedRewards('2018-07-09', '2018-07-16') @@ -51,10 +54,9 @@ async function startProcess () { let end = 0 // Find last saved delegation transaction let lastTx = await collection.find().sort({'tx_date': -1}).limit(1).next() - console.log(lastTx) if (lastTx) end = lastTx.tx_number await updateProperties() - processDelegations(config.account, -1, end) + await processDelegations(config.account, -1, end) let start = moment().utc().startOf('date').subtract(7, 'days').toDate() let txEnd = moment().utc().startOf('date').toDate() processTokenRewards(start, txEnd) @@ -62,15 +64,19 @@ async function startProcess () { } async function processTokenRewards (start, end) { - if (!start) start = moment().utc().startOf('date').subtract(1, 'days').toDate() + if (!start) start = moment().utc().startOf('date').subtract(7, 'days').toDate() if (!end) end = moment().utc().startOf('date').toDate() - let note = 'Delegation Reward Until EOD ' + moment(end).subtract(1, 'days').format('MMMM Do YYYY') + let note = 'Delegation Reward Until EOD ' + moment(end).format('MMMM Do YYYY') let acumulatedSteemPower = await getAcumulatedSteemPower(start, end) + let multiplier = 1 console.log(acumulatedSteemPower) + if (acumulatedSteemPower > config.weekly_rewards_limit) { + multiplier = acumulatedSteemPower / config.weekly_rewards_limit + } for (let user of acumulatedSteemPower.users) { let reward = { user: user.user, - token_count: user.totalSteem, + token_count: +(user.totalSteem * multiplier).toFixed(3), reward_activity: 'Delegation', note: note, date: end @@ -152,14 +158,18 @@ async function processDelegations (account, start, end) { if (delegationTransactions.length > 0) { await collection.insert(delegationTransactions) updateActiveDelegations() - } else console.log('--- No new delegations ---') + } else { + console.log('--- No new delegations ---') + return + } // If more pending delegations call process againg with new index - if (start !== limit && !ended) processDelegations(account, lastTrans, end) + if (start !== limit && !ended) return processDelegations(account, lastTrans, end) // console.log(transactions) + return } catch (err) { console.log(err) // Consider exponential backoff if extreme cases start happening - if (err.type === 'request-timeout' || err.type === 'body-timeout') processDelegations(account, start, end) + if (err.type === 'request-timeout' || err.type === 'body-timeout') return processDelegations(account, start, end) } } diff --git a/package-lock.json b/package-lock.json index 0d1c42a..fcce6df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -868,6 +868,15 @@ "sha.js": "^2.4.8" } }, + "cron-parser": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-2.5.0.tgz", + "integrity": "sha512-gzmXu16/prizIbKPPKJo+WgBpV7k8Rxxu9FgaANW+vx5DebCXavfRqbROjKkr9ETvVPqs+IO+NXj4GG/eLf8zQ==", + "requires": { + "is-nan": "^1.2.1", + "moment-timezone": "^0.5.0" + } + }, "cross-env": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.1.3.tgz", @@ -2903,6 +2912,14 @@ "is-path-inside": "^1.0.0" } }, + "is-nan": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.2.1.tgz", + "integrity": "sha1-n69ltvttskt/XAYoR16nH5iEAeI=", + "requires": { + "define-properties": "^1.1.1" + } + }, "is-npm": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", @@ -3174,6 +3191,11 @@ "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=" }, + "long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ=" + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -3378,6 +3400,14 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" }, + "moment-timezone": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.21.tgz", + "integrity": "sha512-j96bAh4otsgj3lKydm3K7kdtA3iKf2m6MY2iSYCzCm5a1zmHo1g+aK3068dDEeocLZQIS9kU8bsdQHLqEvgW0A==", + "requires": { + "moment": ">= 2.9.0" + } + }, "mongodb": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.10.tgz", @@ -3470,6 +3500,16 @@ "is-stream": "^1.0.1" } }, + "node-schedule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-1.3.0.tgz", + "integrity": "sha512-NNwO9SUPjBwFmPh3vXiPVEhJLn4uqYmZYvJV358SRGM06BR4UoIqxJpeJwDDXB6atULsgQA97MfD1zMd5xsu+A==", + "requires": { + "cron-parser": "^2.4.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.0.0" + } + }, "nodemailer": { "version": "4.6.5", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.6.5.tgz", @@ -4535,6 +4575,11 @@ "kind-of": "^3.2.0" } }, + "sorted-array-functions": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.2.0.tgz", + "integrity": "sha512-sWpjPhIZJtqO77GN+LD8dDsDKcWZ9GCOJNqKzi1tvtjGIzwfoyuRH8S0psunmc6Z5P+qfDqztSbwYR5X/e1UTg==" + }, "source-map": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", diff --git a/package.json b/package.json index bedb2b4..a2a1211 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "lodash": "^4.17.10", "moment": "^2.22.2", "mongodb": "^3.0.10", + "node-schedule": "^1.3.0", "nodemailer": "^4.6.5", "nodemailer-express-handlebars": "^3.0.0", "p-iteration": "^1.1.7", From a5cf05691b7157fb1f61cd6dd9be2a6eb6ae384a Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 6 Aug 2018 00:31:58 +0300 Subject: [PATCH 023/193] New endpoints for various purposes Creating new endpoint for returning total number of rewarded users, along total number of tokens distributed Creating new endpoint for returning total number of rewards sent out Creating new endpoint for supported charities Fixing display of user token count to a max of 3 decimal digits Removing limit of 250 transactions per user display, and constraining this only to per single user --- app.js | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index bdaa59c..5426ede 100644 --- a/app.js +++ b/app.js @@ -49,18 +49,79 @@ app.get('/', function (req, res) { res.send('Hello there!'); }); +/* end point for user total token count display */ app.get('/user/:user', async function (req, res) { let user = await collection.findOne({_id: req.params.user}, {fields : { _id:0} }); console.log(user); + //fixing token amount display for 3 digits + if (typeof user!= "undefined" && user!=null){ + if (typeof user.tokens!= "undefined"){ + user.tokens = user.tokens.toFixed(3) + } + } + res.header('Access-Control-Allow-Origin', '*'); res.send(user); }); + +/* end point for user transactions display (per user or general actifit token transactions, limited by 250 */ app.get('/transactions/:user?', async function (req, res) { let query = {}; - if(req.params.user) + var transactions; + if(req.params.user){ query = {user: req.params.user} - let transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(250).toArray(); + transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + }else{ + //only limit returned transactions in case this is a general query + transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(250).toArray(); + } + res.header('Access-Control-Allow-Origin', '*'); res.send(transactions); }); +/* end point for returning number of awarded users and tokens distributed */ +app.get('/user-tokens-info', async function(req, res) { + + await db.collection(collection_name).aggregate([ + { + $match: {} + }, + { + $group: + { + _id: null, + tokens_distributed: { $sum: "$tokens" }, + user_count: { $sum: 1 } + } + } + ]).toArray(function(err, results) { + var output = 'rewarded users:'+results[0].user_count+','; + output += 'tokens distributed:'+results[0].tokens_distributed; + res.header('Access-Control-Allow-Origin', '*'); + res.send(results); + console.log(results); + }); + +}); + +/* end point for returning count of posts/activities rewarded */ +app.get('/rewarded-activity-count', async function(req, res) { + + await db.collection("posts").aggregate( [ + { $count: "reward_count" } + ]).toArray(function(err, results) { + console.log(results); + utils.log(results, 'rewarded-activity-count'); + res.header('Access-Control-Allow-Origin', '*'); + res.send(results); + }); +}); + +/* end point for returning charity data supported by actifit */ +app.get('/charities', async function (req, res) { + var charities = await db.collection('available_charities').find({status:"enabled"}, {charity_name: 1}).sort({charity_name: 1}).toArray(); + res.header('Access-Control-Allow-Origin', '*'); + res.send(charities); +}); + app.listen(process.env.PORT || 3000); \ No newline at end of file From a1ba305dca62e0b08af8e4388a63ff290e3f1dbd Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 6 Aug 2018 00:38:57 +0300 Subject: [PATCH 024/193] Voting process adjustments adding support for fetching more than 100 posts and upvoting them fix for incorrect sort order for posts adding to logs an automated display of properly markdown formatted output of voted posts along with their ranking (#1, #2,...) for daily report purposes adding support for banned users list preventing upvote of posts which are more than 1.5 days old --- curation-bot.js | 92 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 69033ea..7fe3448 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -54,6 +54,10 @@ startProcess(); // Schedule to run every minute setInterval(startProcess, 60 * 1000); + +var votePosts; +var lastIterationCount = 0; + async function startProcess() { if(!botNames) botNames = await utils.loadBots(); @@ -87,9 +91,12 @@ async function startProcess() { console.log('Voting Power: ' + utils.format(vp / 100) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); // We are at 100% voting power - time to vote! - if (vp >= 9800) { + if (vp >= 10000) { skip = true; - processVotes(); + + var query = {tag: config.main_tag, limit: 100}; + votePosts = Array(); + processVotes(query, false); } } else if(skip) @@ -99,31 +106,39 @@ async function startProcess() { else console.log('Voting... or waiting for a day to pass'); } -function processVotes() { +function processVotes(query, subsequent) { - var query = {tag: config.main_tag, limit: 100}; steem.api.getDiscussionsByCreated(query, function (err, result) { if (result && !err) { is_voting = true; - if(result.length == 0 || !result[0]) { - utils.log('No posts found for this tag: ' + config.main_tag); - last_voted++; - return; - } - var votePosts = Array(); + utils.log(result.length + ' posts to process...'); for(var i = 0; i < result.length; i++) { var post = result[i]; + //if this is a subsequent call, we need to skip first post + if (subsequent && i==0){ + // console.log('skip post:'+post.title); + //continue to next element + continue; + } + + //if this is the last post, save it to skip it in next iteration + if (i == result.length - 1){ + utils.log('storing last post iteration: ' + post.url); + //update query element to include the most recent post for a starting point of the next iteration + query['start_permlink'] = post.permlink; + query['start_author'] = post.author; + } // Make sure the post is less than 6.5 days - if((new Date() - new Date(post.created + 'Z')) >= (6.5 * 24 * 60 * 60 * 1000)) { + /*if((new Date() - new Date(post.created + 'Z')) >= (6.5 * 24 * 60 * 60 * 1000)) { utils.log('This post is too old for a vote: ' + post.url); continue; - } + }*/ - // Make sure the post is older than 24hs + // Make sure the post is older than config time if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { utils.log('This post is too new for a vote: ' + post.url); continue; @@ -189,9 +204,24 @@ function processVotes() { continue; } + //check if user is banned + + for (var n = 0; n < config.banned_users.length; n++) { + if (post.author === config.banned_users[n]){ + utils.log('User '+post.author+' is banned, skipping his post:' + post.url); + continue; + } + } + + + //skip any posts that are more than 1.5 days old + if((new Date() - new Date(post.created + 'Z')) >= (1.5 * 24 * 60 * 60 * 1000)) { + continue; + } + try { post.json = JSON.parse(post.json_metadata); - step_count = post.json.step_count; + var step_count = post.json.step_count; if (step_count < 5000) continue; else if (step_count < 6000) @@ -212,6 +242,7 @@ function processVotes() { continue; } + let last_index = _.findLastIndex(votePosts, ['author', post.author]); if (last_index != -1) { console.log('---- User already has vote ------'); @@ -238,19 +269,33 @@ function processVotes() { } /*let testPost = {rate_multiplier: 0.8}; votePosts.push(testPost);*/ + //if this is the first try, or the new count of posts is bigger than the one before, let's try adding again + if (!subsequent || votePosts.length>lastIterationCount){ + + //update last count + lastIterationCount = votePosts.length; + //call again with subsequent enabled to avoid duplicate posts, disparse the calls by 1 sec to avoid API timeouts + console.log("query:"+query['tag']); + console.log("query:"+query['start_permlink']); + setTimeout(processVotes, 1000, query, true); + + }else{ + + if (votePosts.length > 0) { utils.log(votePosts.length + ' posts to vote...'); vote_data = utils.calculateVotes(votePosts, config.vote_weight); votePosts.sort(function(post1, post2) { // Ascending: first age less than the previous - return post2.json.step_count - post1.json.step_count; + return post1.json.step_count - post2.json.step_count; }); + //utils.log(vote_data.total_votes + ' total votes to divide.'); utils.log(vote_data.power_per_vote + ' power per full vote.'); utils.log(vote_data.power_per_vote * 0.8 + ' power per second vote.'); utils.log(vote_data.power_per_vote * 0.65 + ' power per third vote.'); utils.log(vote_data.power_per_vote * 0.5 + ' power per fourth vote.'); - utils.log(vote_data.power_per_vote * 0.35 + ' power per fith vote.'); + utils.log(vote_data.power_per_vote * 0.35 + ' power per fifth vote.'); utils.log(vote_data.power_per_vote * 0.2 + ' power per lowest vote.'); if(config.testing) return; @@ -264,6 +309,7 @@ function processVotes() { error_sent = true; } } + } last_voted++; } else { console.log(err, result); @@ -271,15 +317,16 @@ function processVotes() { } }); } - +var post_rank = 0; function votingProcess(posts, power_per_vote) { // Get the first bid in the list sendVote(posts.pop(), 2, power_per_vote) .then( res => { // If there are more bids, vote on the next one after 10 seconds if (posts.length > 0) { - setTimeout(function () { votingProcess(posts, power_per_vote); }, 5000); + setTimeout(function () { votingProcess(posts, power_per_vote); }, 10000); } else { + post_rank = 0; setTimeout(function () { utils.log('======================================================='); utils.log('Voting Complete!'); @@ -297,8 +344,13 @@ function votingProcess(posts, power_per_vote) { } function sendVote(post, retries, power_per_vote) { - utils.log('Voting on: ' + post.url); - var vote_weight = Math.floor(post.rate_multiplier * power_per_vote); + utils.log('Voting on: ' + post.url + ' with count'+post.json.step_count); + var token_count = parseFloat(post.rate_multiplier)*100; + + var vote_weight = Math.ceil(post.rate_multiplier * power_per_vote); + post_rank += 1; + utils.log('|#'+post_rank+'|@'+post.author+'|'+ post.json.step_count +'|'+token_count+' Tokens|'+utils.format(vote_weight / 100)+'%|[post](https://www.steemit.com'+post.url+')'); + if (vote_weight > 10000) vote_weight = 10000; post.vote_weight = vote_weight; From 14d35befd3269a1949a5eab37c80f5012a67ea23 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 6 Aug 2018 00:43:43 +0300 Subject: [PATCH 025/193] Charity support and additional fixes adding support for rewarding charity activity via assigning actifit token rewarded account as the included charity appending banned users check on utils level couple of additional fixes regarding the saving process --- save-data.js | 33 ++++++++++++++++++++++++--------- utils.js | 7 +++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/save-data.js b/save-data.js index 34f766e..d7a436a 100644 --- a/save-data.js +++ b/save-data.js @@ -38,7 +38,7 @@ MongoClient.connect(url, function(err, client) { setInterval(updateUserTokens, 450 * 1000); } else { utils.log(err, 'import'); - mail.sendPlainMail('Database Error', err, 'cryptouru@gmail.com') + //mail.sendPlainMail('Database Error', err, '') .then(function(res, err) { if (!err) { console.log(res); @@ -109,10 +109,11 @@ function getPosts(index) { let last_post = posts[posts.length - 1]; await processTransactions(posts); console.log('Inserted transactions'); + //appending fix for potential caught loop + postsProcessing = false; if (!index || (index.start_permlink != last_post.permlink && index.start_author != last_post.author && result.length >= 100)) return getPosts({start_author: last_post.author, start_permlink: last_post.permlink}); console.log('No more new posts'); - postsProcessing = false; return; }) .catch(function (err) { @@ -150,12 +151,23 @@ async function processTransactions(posts) { let collection = db.collection('token_transactions'); var bulk = collection.initializeUnorderedBulkOp(); await forEach(posts, async (post) => { + //by default the reward owner is the author + var reward_user = post.author; + var activity_type = 'Post'; + var note = ''; + //if we find this is a charity run, let's switch it to the actual charity name + if (typeof post.json_metadata.charity != 'undefined' && post.json_metadata.charity != '' && post.json_metadata.charity != 'undefined'){ + reward_user = post.json_metadata.charity; + activity_type = 'Charity Post'; + note = 'Charity donation via activity by user '+post.author; + } let post_transaction = { - user: post.author, - reward_activity: 'Post', + user: reward_user, + reward_activity: activity_type, token_count: post.token_rewards, url: post.url, - date: post.created + date: post.created, + note: note } bulk.find( { @@ -223,10 +235,13 @@ async function updateUserTokens() { } } ]) - let user_tokens = await query.toArray(); - await db.collection('user_tokens').remove({}); - return await db.collection('user_tokens').insert(user_tokens); - + try{ + let user_tokens = await query.toArray(); + await db.collection('user_tokens').remove({}); + return await db.collection('user_tokens').insert(user_tokens); + }catch(err){ + console.log('>>save data error:'+err.message); + } } async function processVotedPosts() { diff --git a/utils.js b/utils.js index dc05cc9..88fdc78 100644 --- a/utils.js +++ b/utils.js @@ -210,6 +210,13 @@ function format(n, c, d, t) { if(!benefit) continue; + for (var n = 0; n < config.banned_users.length; n++) { + if (post.author === config.banned_users[n]){ + console.log('User '+post.author+' is banned, skipping his post:' + post.url); + continue; + } + } + results.push(post); } return results; From 56e51ad5ca28dc840d561fd30ac742e115bd8512 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Tue, 7 Aug 2018 15:06:02 +0300 Subject: [PATCH 026/193] New config items adding new VP min limit to kick start voting adding new max limit percent upvote per post increasing retries min to 20 and adjusting relevant log --- config-example.json | 50 +- curation-bot.js | 1138 ++++++++++++++++++++++--------------------- utils.js | 577 +++++++++++----------- 3 files changed, 886 insertions(+), 879 deletions(-) diff --git a/config-example.json b/config-example.json index c1d9e34..d14cd10 100644 --- a/config-example.json +++ b/config-example.json @@ -1,24 +1,26 @@ -{ - "disabled_mode": false, - "testing": true, - "detailed_logging": false, - "account": "actifit", - "main_tag": "actifit", - "memo_key": "your_private_memo_key", - "posting_key": "your_private_posting_key", - "active_key": "your_private_active_key", - "auto_claim_rewards" : false, - "vote_weight": 100000, - "min_hours": 24, - "comment_location": "comment.md", - "comment": false, - "resteem": false, - "no_paid_bots": true, - "beneficiaries": ["actifit", "actifit.pay"], - "flag_signal_accounts": ["spaminator", "cheetah", "steemcleaners", "mack-bot"], - "mongo_uri": "mongodb://localhost:27017", - "report_emails": "report@email.com", - "smtp_usr": "SMTP USER NAME", - "smtp_from": "'FROM NANE' ", - "smtp_key": "smtp_key_or_password" -} +{ + "disabled_mode": false, + "testing": true, + "detailed_logging": false, + "account": "actifit", + "main_tag": "actifit", + "memo_key": "your_private_memo_key", + "posting_key": "your_private_posting_key", + "active_key": "your_private_active_key", + "auto_claim_rewards" : false, + "vote_weight": 100000, + "vp_kickstart": 10000, + "max_vote_per_post": 3000, + "min_hours": 24, + "comment_location": "comment.md", + "comment": false, + "resteem": false, + "no_paid_bots": true, + "beneficiaries": ["actifit", "actifit.pay"], + "flag_signal_accounts": ["spaminator", "cheetah", "steemcleaners", "mack-bot"], + "mongo_uri": "mongodb://localhost:27017", + "report_emails": "report@email.com", + "smtp_usr": "SMTP USER NAME", + "smtp_from": "'FROM NANE' ", + "smtp_key": "smtp_key_or_password" +} diff --git a/curation-bot.js b/curation-bot.js index 7fe3448..0dd0742 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1,568 +1,570 @@ -var fs = require("fs"); -const steem = require('steem'); -var utils = require('./utils'); -var mail = require('./mail'); -var _ = require('lodash'); -var moment = require('moment'); - -var account = null; -var last_trans = 0; -var members = []; -var whitelist = []; -var config = null; -var first_load = true; -var is_voting = false; -var last_voted = 0; -var vote_time; -var last_votes = Array(); -var skip = false; -var version = '0.0.1'; -var error_sent = false; - -steem.api.setOptions({ url: 'https://api.steemit.com' }); - -utils.log("* START - Version: " + version + " *"); - -// Load the settings from the config file -loadConfig(); -var botNames; - -// Check if bot state has been saved to disk, in which case load it -if (fs.existsSync('state.json')) { - var state = JSON.parse(fs.readFileSync("state.json")); - - if (state.last_trans) - last_trans = state.last_trans; - - if (state.last_voted) - last_voted = state.last_voted; - - if (state.vote_time) - vote_time = state.vote_time; - - utils.log('Restored saved bot state: ' + JSON.stringify(state)); -} - -// Check if members list has been saved to disk, in which case load it -if (fs.existsSync('members.json')) { - var members_file = JSON.parse(fs.readFileSync("members.json")); - members = members_file.members; - utils.log('Loaded ' + members.length + ' members.'); -} - -startProcess(); -// Schedule to run every minute -setInterval(startProcess, 60 * 1000); - - -var votePosts; -var lastIterationCount = 0; - -async function startProcess() { - if(!botNames) - botNames = await utils.loadBots(); - if (config.detailed_logging) - console.log('Start process'); - // Load the settings from the config file each time so we can pick up any changes - loadConfig(); - - // Load the bot account info - steem.api.getAccounts([config.account], function (err, result) { - if (err || !result) - console.log(err, result); - else { - account = result[0]; - - // Check if there are any rewards to claim. - claimRewards(); - } - }); - - var oneMoreDay = new Date(new Date(vote_time).getTime() + (24 * 60 * 60 * 1000)); - var today = new Date(); - var passedOneDay = today >= oneMoreDay; - - if (account && !skip && !is_voting && passedOneDay) { - // Load the current voting power of the account - var vp = utils.getVotingPower(account); - - if (config.detailed_logging) - utils.log('Voting Power: ' + utils.format(vp / 100) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); - - console.log('Voting Power: ' + utils.format(vp / 100) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); - // We are at 100% voting power - time to vote! - if (vp >= 10000) { - skip = true; - - var query = {tag: config.main_tag, limit: 100}; - votePosts = Array(); - processVotes(query, false); - } - - } else if(skip) - skip = false; - else if (!account) - console.log('Loading account data...'); - else console.log('Voting... or waiting for a day to pass'); -} - -function processVotes(query, subsequent) { - - - steem.api.getDiscussionsByCreated(query, function (err, result) { - if (result && !err) { - is_voting = true; - - utils.log(result.length + ' posts to process...'); - - for(var i = 0; i < result.length; i++) { - var post = result[i]; - - //if this is a subsequent call, we need to skip first post - if (subsequent && i==0){ - // console.log('skip post:'+post.title); - //continue to next element - continue; - } - - //if this is the last post, save it to skip it in next iteration - if (i == result.length - 1){ - utils.log('storing last post iteration: ' + post.url); - //update query element to include the most recent post for a starting point of the next iteration - query['start_permlink'] = post.permlink; - query['start_author'] = post.author; - } - // Make sure the post is less than 6.5 days - /*if((new Date() - new Date(post.created + 'Z')) >= (6.5 * 24 * 60 * 60 * 1000)) { - utils.log('This post is too old for a vote: ' + post.url); - continue; - }*/ - - // Make sure the post is older than config time - if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { - utils.log('This post is too new for a vote: ' + post.url); - continue; - } - - // Check if the bot already voted on this post - if(post.active_votes.find(v => v.voter == 'actifit')) { - utils.log('Bot already voted on: ' + post.url); - continue; - } - - // Check if any tags on this post are blacklisted in the settings - if ((config.blacklisted_tags && config.blacklisted_tags.length > 0) || (config.whitelisted_tags && config.whitelisted_tags.length > 0) && post.json_metadata && post.json_metadata != '') { - var tags = JSON.parse(post.json_metadata).tags; - - if((config.blacklisted_tags && config.blacklisted_tags.length > 0) && tags && tags.length > 0 && tags.find(t => config.blacklisted_tags.indexOf(t) >= 0)) { - utils.log('Post contains one or more blacklisted tags. ' + post.url); - continue; - } - - if((config.whitelisted_tags && config.whitelisted_tags.length > 0) && tags && tags.length > 0 && !tags.find(t => config.whitelisted_tags.indexOf(t) >= 0)) { - utils.log('Post does not contain a whitelisted tag. ' + post.url); - continue; - } - } - - // Check if post category is main tag - if (post.category != config.main_tag) { - utils.log('Post does not match category tag. ' + post.url); - continue; - } - - // Check if this post has been flagged by any flag signal accounts - if(config.flag_signal_accounts) { - if(post.active_votes.find(function(v) { return v.percent < 0 && config.flag_signal_accounts.indexOf(v.voter) >= 0; })) { - utils.log('Post was downvoted by a flag signal account. ' + post.url); - continue; - } - } - - // Check if this post has been voted by any type of paid bot - if(botNames && config.no_paid_bots) { - if(post.active_votes.find(function(v) { return botNames.includes(v.voter); })) { - utils.log('Post was vote by a paid bot account. ' + post.url); - continue; - } - } - - // Check if account is beneficiary - var benefit = 0; - for (var x = 0; x < post.beneficiaries.length; x++) { - for (var n = 0; n < config.beneficiaries.length; n++) { - if (post.beneficiaries[x].account === config.beneficiaries[n]) - benefit ++; - } - if (benefit === config.beneficiaries.length) { - benefit = true; - break; - } - } - if (!benefit) { - utils.log('Post does not match account beneficiary. ' + post.url); - continue; - } - - //check if user is banned - - for (var n = 0; n < config.banned_users.length; n++) { - if (post.author === config.banned_users[n]){ - utils.log('User '+post.author+' is banned, skipping his post:' + post.url); - continue; - } - } - - - //skip any posts that are more than 1.5 days old - if((new Date() - new Date(post.created + 'Z')) >= (1.5 * 24 * 60 * 60 * 1000)) { - continue; - } - - try { - post.json = JSON.parse(post.json_metadata); - var step_count = post.json.step_count; - if (step_count < 5000) - continue; - else if (step_count < 6000) - post.rate_multiplier = 0.2; - else if(step_count < 7000) - post.rate_multiplier = 0.35; - else if(step_count < 8000) - post.rate_multiplier = 0.5; - else if(step_count < 9000) - post.rate_multiplier = 0.65; - else if(step_count < 10000) - post.rate_multiplier = 0.8; - else - post.rate_multiplier = 1; - } catch (err) { - utils.log('Error parsing json metadata'); - console.log(err); - continue; - } - - - let last_index = _.findLastIndex(votePosts, ['author', post.author]); - if (last_index != -1) { - console.log('---- User already has vote ------'); - let last_voted = votePosts[last_index]; - var last_date = moment(last_voted.created).format('D'); - var this_date = moment(post.created).format('D'); - if (last_date != this_date) { - console.log('Voting on: ' + post.url); - votePosts.push(post); - } else { - console.log('---- Last voted -----'); - console.log(new Date (last_voted.created)); - console.log('---- This voted -----'); - console.log(new Date (post.created)); - console.log('---- Moment-----'); - console.log(last_date); - console.log(this_date); - } - - } else { - console.log('Voting on: ' + post.url); - votePosts.push(post); - } - } - /*let testPost = {rate_multiplier: 0.8}; - votePosts.push(testPost);*/ - //if this is the first try, or the new count of posts is bigger than the one before, let's try adding again - if (!subsequent || votePosts.length>lastIterationCount){ - - //update last count - lastIterationCount = votePosts.length; - //call again with subsequent enabled to avoid duplicate posts, disparse the calls by 1 sec to avoid API timeouts - console.log("query:"+query['tag']); - console.log("query:"+query['start_permlink']); - setTimeout(processVotes, 1000, query, true); - - }else{ - - - if (votePosts.length > 0) { - utils.log(votePosts.length + ' posts to vote...'); - vote_data = utils.calculateVotes(votePosts, config.vote_weight); - votePosts.sort(function(post1, post2) { - // Ascending: first age less than the previous - return post1.json.step_count - post2.json.step_count; - }); - - //utils.log(vote_data.total_votes + ' total votes to divide.'); - utils.log(vote_data.power_per_vote + ' power per full vote.'); - utils.log(vote_data.power_per_vote * 0.8 + ' power per second vote.'); - utils.log(vote_data.power_per_vote * 0.65 + ' power per third vote.'); - utils.log(vote_data.power_per_vote * 0.5 + ' power per fourth vote.'); - utils.log(vote_data.power_per_vote * 0.35 + ' power per fifth vote.'); - utils.log(vote_data.power_per_vote * 0.2 + ' power per lowest vote.'); - if(config.testing) - return; - else - votingProcess(votePosts, vote_data.power_per_vote); - - } else { - utils.log('No posts to vote...'); - if(!error_sent) { - errorEmail('No posts to vote...', config.report_emails); - error_sent = true; - } - } - } - last_voted++; - } else { - console.log(err, result); - errorEmail(err, config.report_emails); - } - }); -} -var post_rank = 0; -function votingProcess(posts, power_per_vote) { - // Get the first bid in the list - sendVote(posts.pop(), 2, power_per_vote) - .then( res => { - // If there are more bids, vote on the next one after 10 seconds - if (posts.length > 0) { - setTimeout(function () { votingProcess(posts, power_per_vote); }, 10000); - } else { - post_rank = 0; - setTimeout(function () { - utils.log('======================================================='); - utils.log('Voting Complete!'); - utils.log('======================================================='); - is_voting = false; - error_sent = false; - saveState(); - //reportEmail(config.report_emails) - }, 5000); - } - }) - .catch(err => { - console.log(err); - }) -} - -function sendVote(post, retries, power_per_vote) { - utils.log('Voting on: ' + post.url + ' with count'+post.json.step_count); - var token_count = parseFloat(post.rate_multiplier)*100; - - var vote_weight = Math.ceil(post.rate_multiplier * power_per_vote); - post_rank += 1; - utils.log('|#'+post_rank+'|@'+post.author+'|'+ post.json.step_count +'|'+token_count+' Tokens|'+utils.format(vote_weight / 100)+'%|[post](https://www.steemit.com'+post.url+')'); - - if (vote_weight > 10000) - vote_weight = 10000; - post.vote_weight = vote_weight; - last_votes.push(post); - - return new Promise((resolve, reject) => { - steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { - if (!err && result) { - utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); - - if(config.comment_location && config.comment) - setTimeout(function () { - sendComment(post.author, post.permlink, vote_weight, post.rate_multiplier, post.json.step_count) - .then( res => { - resolve(res) - }) - .catch(err => { - reject(err); - }) - }, 10000); - else - resolve(result); - } else { - utils.log(err, result); - - // Try again one time on error - if (retries < 1) - sendVote(post, retries + 1); - else { - var message = '============= Vote transaction failed two times for: ' + post.url + ' ===============' - utils.log(message); - reject(err); - //errorEmail(message, config.report_emails); - } - } - }); - }); -} - -function sendComment(parentAuthor, parentPermlink, vote_weight, rate_multiplier, post_step_count) { - var content = null; - // Return promise - return new Promise((resolve, reject) => { - content = fs.readFileSync(config.comment_location, "utf8"); - - // If promotion content is specified in the config then use it to comment on the upvoted post - if (content && content != '') { - - // Generate the comment permlink via steemit standard convention - var permlink = 're-' + parentAuthor.replace(/\./g, '') + '-' + parentPermlink + '-' + new Date().toISOString().replace(/-|:|\./g, '').toLowerCase(); - - var token_count = parseFloat(rate_multiplier)*100; - var milestone_txt = "level 1 milestone"; - if(token_count < 36) - milestone_txt = "level 2 milestone"; - else if(token_count < 51) - milestone_txt = "level 3 milestone"; - else if(token_count < 66) - milestone_txt = "level 4 milestone"; - else if(token_count < 81) - milestone_txt = "level 5 milestone"; - else - milestone_txt = "the top level milestone"; - - - // Replace variables in the promotion content - content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{milestone\}/g, milestone_txt).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); - - - // Broadcast the comment - steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, '{"app":"communitybot/' + version + '"}', function (err, result) { - if (!err && result) { - utils.log('Posted comment: ' + permlink); - resolve(result); - } else { - utils.log('Error posting comment: ' + permlink); - reject(err); - } - }); - } else - reject('Failed to load content'); -}); - // Check if the bot should resteem this post - /* if (config.resteem) - resteem(parentAuthor, parentPermlink); */ -} - -function reportEmail(to) { - - var data = {}; - data.posts = last_votes; - data.total_votes = _.sumBy(last_votes, 'net_votes'); - data.total_money = _.sumBy(last_votes, 'vote_weight'); - - mail.sendWithTemplate('Report Mail', data, to, 'votes'); - last_votes = Array(); - -} - -function errorEmail(message, to) { - - mail.sendPlainMail('Info Mail', message, to) - .then(function(res, err) { - if (!err) { - console.log(res); - } else { - console.log(err); - } - }); -} - -function resteem(author, permlink) { - var json = JSON.stringify(['reblog', { - account: config.account, - author: author, - permlink: permlink - }]); - - steem.broadcast.customJson(config.posting_key, [], [config.account], 'follow', json, (err, result) => { - if (!err && result) { - utils.log('Resteemed Post: @' + author + '/' + permlink); - } else { - utils.log('Error resteeming post: @' + author + '/' + permlink); - } - }); -} - -function saveState() { - var state = { - last_trans: last_trans, - last_voted: last_voted, - vote_time: new Date() - }; - - // Save the state of the bot to disk - fs.writeFile('state.json', JSON.stringify(state), function (err) { - if (err) - utils.log(err); - }); -} - -function loadConfig() { - config = JSON.parse(fs.readFileSync("config.json")); -} - -function sendPayment(to, amount, currency, reason, retries, data) { - if(!retries) - retries = 0; - - // Make sure the recipient isn't on the no-refund list (for exchanges and things like that). - if (reason != 'forward_payment' && config.no_refund && config.no_refund.indexOf(to) >= 0) { - utils.log("Payment not sent to: @" + to + " for: " + reason + ' because they are on the no_refund list.'); - return; - } - - // Replace variables in the memo text - var memo = config.transfer_memos[reason]; - memo = memo.replace(/{amount}/g, utils.format(amount, 3) + ' ' + currency); - memo = memo.replace(/{currency}/g, currency); - memo = memo.replace(/{account}/g, config.account); - memo = memo.replace(/{to}/g, to); - memo = memo.replace(/{tag}/g, data); - - // Issue the payment. - steem.broadcast.transfer(config.active_key, config.account, to, utils.format(amount, 3) + ' ' + currency, memo, function (err, response) { - if (err) { - utils.log('Error sending payment to @' + to + ' for: ' + amount + ' ' + currency + ', Error: ' + err); - - // Try again on error - if(retries < 2) - setTimeout(function() { refund(to, amount, currency, reason, retries + 1, data) }, (Math.floor(Math.random() * 10) + 3) * 1000); - else - utils.log('============= Payment failed three times for: @' + to + ' ==============='); - } else { - utils.log('Payment of ' + amount + ' ' + currency + ' sent to @' + to + ' for reason: ' + reason); - } - }); -} - -function claimRewards() { - if (!config.auto_claim_rewards) - return; - - // Make api call only if you have actual reward - if (parseFloat(account.reward_steem_balance) > 0 || parseFloat(account.reward_sbd_balance) > 0 || parseFloat(account.reward_vesting_balance) > 0) { - steem.broadcast.claimRewardBalance(config.posting_key, config.account, account.reward_steem_balance, account.reward_sbd_balance, account.reward_vesting_balance, function (err, result) { - if (err) { - utils.log(err); - } - - if (result) { - - var rewards_message = "$$$ ==> Rewards Claim"; - if (parseFloat(account.reward_sbd_balance) > 0) { rewards_message = rewards_message + ' SBD: ' + parseFloat(account.reward_sbd_balance); } - if (parseFloat(account.reward_steem_balance) > 0) { rewards_message = rewards_message + ' STEEM: ' + parseFloat(account.reward_steem_balance); } - if (parseFloat(account.reward_vesting_balance) > 0) { rewards_message = rewards_message + ' VESTS: ' + parseFloat(account.reward_vesting_balance); } - - utils.log(rewards_message); - - // If there are liquid post rewards, withdraw them to the specified account - if (parseFloat(account.reward_sbd_balance) > 0 && config.post_rewards_withdrawal_account && config.post_rewards_withdrawal_account != '') { - - // Send liquid post rewards to the specified account - steem.broadcast.transfer(config.active_key, config.account, config.post_rewards_withdrawal_account, account.reward_sbd_balance, 'Liquid Post Rewards Withdrawal', function (err, response) { - if (err) - utils.log(err, response); - else { - utils.log('$$$ Auto withdrawal - liquid post rewards: ' + account.reward_sbd_balance + ' sent to @' + config.post_rewards_withdrawal_account); - } - }); - } - } - }); - } -} +var fs = require("fs"); +const steem = require('steem'); +var utils = require('./utils'); +var mail = require('./mail'); +var _ = require('lodash'); +var moment = require('moment'); + +var account = null; +var last_trans = 0; +var members = []; +var whitelist = []; +var config = null; +var first_load = true; +var is_voting = false; +var last_voted = 0; +var vote_time; +var last_votes = Array(); +var skip = false; +var version = '0.0.1'; +var error_sent = false; + +steem.api.setOptions({ url: 'https://api.steemit.com' });//https://gtg.steem.house:8090 + +utils.log("* START - Version: " + version + " *"); + +// Load the settings from the config file +loadConfig(); +var botNames; + +// Check if bot state has been saved to disk, in which case load it +if (fs.existsSync('state.json')) { + var state = JSON.parse(fs.readFileSync("state.json")); + + if (state.last_trans) + last_trans = state.last_trans; + + if (state.last_voted) + last_voted = state.last_voted; + + if (state.vote_time) + vote_time = state.vote_time; + + utils.log('Restored saved bot state: ' + JSON.stringify(state)); +} + +// Check if members list has been saved to disk, in which case load it +if (fs.existsSync('members.json')) { + var members_file = JSON.parse(fs.readFileSync("members.json")); + members = members_file.members; + utils.log('Loaded ' + members.length + ' members.'); +} + +startProcess(); +// Schedule to run every minute +setInterval(startProcess, 60 * 1000); + + +var votePosts; +var lastIterationCount = 0; + +async function startProcess() { + if(!botNames) + botNames = await utils.loadBots(); + if (config.detailed_logging) + console.log('Start process'); + // Load the settings from the config file each time so we can pick up any changes + loadConfig(); + + // Load the bot account info + steem.api.getAccounts([config.account], function (err, result) { + if (err || !result) + console.log(err, result); + else { + account = result[0]; + + // Check if there are any rewards to claim. + claimRewards(); + } + }); + + var oneMoreDay = new Date(new Date(vote_time).getTime() + (24 * 60 * 60 * 1000)); + var today = new Date(); + //deactivating condition of 24 hrs to pass + var passedOneDay = true;//today >= oneMoreDay; + + if (account && !skip && !is_voting && passedOneDay) { + // Load the current voting power of the account + var vp = utils.getVotingPower(account); + + if (config.detailed_logging) + utils.log('Voting Power: ' + utils.format(vp / 100) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); + + console.log('Voting Power: ' + utils.format(vp / 100) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); + // We are at voting power kick start - time to vote! + if (vp >= config.vp_kickstart) { + skip = true; + + var query = {tag: config.main_tag, limit: 100}; + votePosts = Array(); + processVotes(query, false); + } + + } else if(skip) + skip = false; + else if (!account) + console.log('Loading account data...'); + else console.log('Voting... or waiting for a day to pass'); +} + +function processVotes(query, subsequent) { + + + steem.api.getDiscussionsByCreated(query, function (err, result) { + if (result && !err) { + is_voting = true; + + utils.log(result.length + ' posts to process...'); + + for(var i = 0; i < result.length; i++) { + var post = result[i]; + + //if this is a subsequent call, we need to skip first post + if (subsequent && i==0){ + // console.log('skip post:'+post.title); + //continue to next element + continue; + } + + //if this is the last post, save it to skip it in next iteration + if (i == result.length - 1){ + utils.log('storing last post iteration: ' + post.url); + //update query element to include the most recent post for a starting point of the next iteration + query['start_permlink'] = post.permlink; + query['start_author'] = post.author; + } + // Make sure the post is less than 6.5 days + /*if((new Date() - new Date(post.created + 'Z')) >= (6.5 * 24 * 60 * 60 * 1000)) { + utils.log('This post is too old for a vote: ' + post.url); + continue; + }*/ + + // Make sure the post is older than config time + if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { + utils.log('This post is too new for a vote: ' + post.url); + continue; + } + + // Check if the bot already voted on this post + if(post.active_votes.find(v => v.voter == 'actifit')) { + utils.log('Bot already voted on: ' + post.url); + continue; + } + + // Check if any tags on this post are blacklisted in the settings + if ((config.blacklisted_tags && config.blacklisted_tags.length > 0) || (config.whitelisted_tags && config.whitelisted_tags.length > 0) && post.json_metadata && post.json_metadata != '') { + var tags = JSON.parse(post.json_metadata).tags; + + if((config.blacklisted_tags && config.blacklisted_tags.length > 0) && tags && tags.length > 0 && tags.find(t => config.blacklisted_tags.indexOf(t) >= 0)) { + utils.log('Post contains one or more blacklisted tags. ' + post.url); + continue; + } + + if((config.whitelisted_tags && config.whitelisted_tags.length > 0) && tags && tags.length > 0 && !tags.find(t => config.whitelisted_tags.indexOf(t) >= 0)) { + utils.log('Post does not contain a whitelisted tag. ' + post.url); + continue; + } + } + + // Check if post category is main tag + if (post.category != config.main_tag) { + utils.log('Post does not match category tag. ' + post.url); + continue; + } + + // Check if this post has been flagged by any flag signal accounts + if(config.flag_signal_accounts) { + if(post.active_votes.find(function(v) { return v.percent < 0 && config.flag_signal_accounts.indexOf(v.voter) >= 0; })) { + utils.log('Post was downvoted by a flag signal account. ' + post.url); + continue; + } + } + + // Check if this post has been voted by any type of paid bot + if(botNames && config.no_paid_bots) { + if(post.active_votes.find(function(v) { return botNames.includes(v.voter); })) { + utils.log('Post was vote by a paid bot account. ' + post.url); + continue; + } + } + + // Check if account is beneficiary + var benefit = 0; + for (var x = 0; x < post.beneficiaries.length; x++) { + for (var n = 0; n < config.beneficiaries.length; n++) { + if (post.beneficiaries[x].account === config.beneficiaries[n]) + benefit ++; + } + if (benefit === config.beneficiaries.length) { + benefit = true; + break; + } + } + if (!benefit) { + utils.log('Post does not match account beneficiary. ' + post.url); + continue; + } + + //check if user is banned + + for (var n = 0; n < config.banned_users.length; n++) { + if (post.author === config.banned_users[n]){ + utils.log('User '+post.author+' is banned, skipping his post:' + post.url); + continue; + } + } + + + //skip any posts that are more than 1.5 days old + if((new Date() - new Date(post.created + 'Z')) >= (1.5 * 24 * 60 * 60 * 1000)) { + continue; + } + + try { + post.json = JSON.parse(post.json_metadata); + var step_count = post.json.step_count; + if (step_count < 5000) + continue; + else if (step_count < 6000) + post.rate_multiplier = 0.2; + else if(step_count < 7000) + post.rate_multiplier = 0.35; + else if(step_count < 8000) + post.rate_multiplier = 0.5; + else if(step_count < 9000) + post.rate_multiplier = 0.65; + else if(step_count < 10000) + post.rate_multiplier = 0.8; + else + post.rate_multiplier = 1; + } catch (err) { + utils.log('Error parsing json metadata'); + console.log(err); + continue; + } + + + let last_index = _.findLastIndex(votePosts, ['author', post.author]); + if (last_index != -1) { + console.log('---- User already has vote ------'); + let last_voted = votePosts[last_index]; + var last_date = moment(last_voted.created).format('D'); + var this_date = moment(post.created).format('D'); + if (last_date != this_date) { + console.log('Voting on: ' + post.url); + votePosts.push(post); + } else { + console.log('---- Last voted -----'); + console.log(new Date (last_voted.created)); + console.log('---- This voted -----'); + console.log(new Date (post.created)); + console.log('---- Moment-----'); + console.log(last_date); + console.log(this_date); + } + + } else { + console.log('Voting on: ' + post.url); + votePosts.push(post); + } + } + /*let testPost = {rate_multiplier: 0.8}; + votePosts.push(testPost);*/ + //if this is the first try, or the new count of posts is bigger than the one before, let's try adding again + if (!subsequent || votePosts.length>lastIterationCount){ + + //update last count + lastIterationCount = votePosts.length; + //call again with subsequent enabled to avoid duplicate posts, disparse the calls by 1 sec to avoid API timeouts + console.log("query:"+query['tag']); + console.log("query:"+query['start_permlink']); + setTimeout(processVotes, 1000, query, true); + + }else{ + + + if (votePosts.length > 0) { + utils.log(votePosts.length + ' posts to vote...'); + vote_data = utils.calculateVotes(votePosts, config.vote_weight); + votePosts.sort(function(post1, post2) { + // Ascending: first age less than the previous + return post1.json.step_count - post2.json.step_count; + }); + + //utils.log(vote_data.total_votes + ' total votes to divide.'); + utils.log(vote_data.power_per_vote + ' power per full vote.'); + utils.log(vote_data.power_per_vote * 0.8 + ' power per second vote.'); + utils.log(vote_data.power_per_vote * 0.65 + ' power per third vote.'); + utils.log(vote_data.power_per_vote * 0.5 + ' power per fourth vote.'); + utils.log(vote_data.power_per_vote * 0.35 + ' power per fifth vote.'); + utils.log(vote_data.power_per_vote * 0.2 + ' power per lowest vote.'); + if(config.testing) + return; + else + votingProcess(votePosts, vote_data.power_per_vote); + + } else { + utils.log('No posts to vote...'); + if(!error_sent) { + //errorEmail('No posts to vote...', config.report_emails); + error_sent = true; + } + } + } + last_voted++; + } else { + console.log(err, result); + //errorEmail(err, config.report_emails); + } + }); +} +var post_rank = 0; +function votingProcess(posts, power_per_vote) { + // Get the first bid in the list + sendVote(posts.pop(), 20, power_per_vote) + .then( res => { + // If there are more bids, vote on the next one after 10 seconds + if (posts.length > 0) { + setTimeout(function () { votingProcess(posts, power_per_vote); }, 10000); + } else { + post_rank = 0; + setTimeout(function () { + utils.log('======================================================='); + utils.log('Voting Complete!'); + utils.log('======================================================='); + is_voting = false; + error_sent = false; + saveState(); + //reportEmail(config.report_emails) + }, 5000); + } + }) + .catch(err => { + console.log(err); + }) +} + +function sendVote(post, retries, power_per_vote) { + utils.log('Voting on: ' + post.url + ' with count'+post.json.step_count); + var token_count = parseFloat(post.rate_multiplier)*100; + + var vote_weight = Math.ceil(post.rate_multiplier * power_per_vote); + post_rank += 1; + utils.log('|#'+post_rank+'|@'+post.author+'|'+ post.json.step_count +'|'+token_count+' Tokens|'+utils.format(vote_weight / 100)+'%|[post](https://www.steemit.com'+post.url+')'); + + if (vote_weight > config.max_vote_per_post){ + vote_weight = config.max_vote_per_post; + } + post.vote_weight = vote_weight; + last_votes.push(post); + + return new Promise((resolve, reject) => { + steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { + if (!err && result) { + utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); + + if(config.comment_location && config.comment) + setTimeout(function () { + sendComment(post.author, post.permlink, vote_weight, post.rate_multiplier, post.json.step_count) + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, 10000); + else + resolve(result); + } else { + utils.log(err, result); + + // Try again one time on error + if (retries < 1) + sendVote(post, retries + 1); + else { + var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' + utils.log(message); + reject(err); + //errorEmail(message, config.report_emails); + } + } + }); + }); +} + +function sendComment(parentAuthor, parentPermlink, vote_weight, rate_multiplier, post_step_count) { + var content = null; + // Return promise + return new Promise((resolve, reject) => { + content = fs.readFileSync(config.comment_location, "utf8"); + + // If promotion content is specified in the config then use it to comment on the upvoted post + if (content && content != '') { + + // Generate the comment permlink via steemit standard convention + var permlink = 're-' + parentAuthor.replace(/\./g, '') + '-' + parentPermlink + '-' + new Date().toISOString().replace(/-|:|\./g, '').toLowerCase(); + + var token_count = parseFloat(rate_multiplier)*100; + var milestone_txt = "level 1 milestone"; + if(token_count < 36) + milestone_txt = "level 2 milestone"; + else if(token_count < 51) + milestone_txt = "level 3 milestone"; + else if(token_count < 66) + milestone_txt = "level 4 milestone"; + else if(token_count < 81) + milestone_txt = "level 5 milestone"; + else + milestone_txt = "the top level milestone"; + + + // Replace variables in the promotion content + content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{milestone\}/g, milestone_txt).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); + + + // Broadcast the comment + steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, '{"app":"communitybot/' + version + '"}', function (err, result) { + if (!err && result) { + utils.log('Posted comment: ' + permlink); + resolve(result); + } else { + utils.log('Error posting comment: ' + permlink); + reject(err); + } + }); + } else + reject('Failed to load content'); +}); + // Check if the bot should resteem this post + /* if (config.resteem) + resteem(parentAuthor, parentPermlink); */ +} + +function reportEmail(to) { + + var data = {}; + data.posts = last_votes; + data.total_votes = _.sumBy(last_votes, 'net_votes'); + data.total_money = _.sumBy(last_votes, 'vote_weight'); + + mail.sendWithTemplate('Report Mail', data, to, 'votes'); + last_votes = Array(); + +} + +function errorEmail(message, to) { + + mail.sendPlainMail('Info Mail', message, to) + .then(function(res, err) { + if (!err) { + console.log(res); + } else { + console.log(err); + } + }); +} + +function resteem(author, permlink) { + var json = JSON.stringify(['reblog', { + account: config.account, + author: author, + permlink: permlink + }]); + + steem.broadcast.customJson(config.posting_key, [], [config.account], 'follow', json, (err, result) => { + if (!err && result) { + utils.log('Resteemed Post: @' + author + '/' + permlink); + } else { + utils.log('Error resteeming post: @' + author + '/' + permlink); + } + }); +} + +function saveState() { + var state = { + last_trans: last_trans, + last_voted: last_voted, + vote_time: new Date() + }; + + // Save the state of the bot to disk + fs.writeFile('state.json', JSON.stringify(state), function (err) { + if (err) + utils.log(err); + }); +} + +function loadConfig() { + config = JSON.parse(fs.readFileSync("config.json")); +} + +function sendPayment(to, amount, currency, reason, retries, data) { + if(!retries) + retries = 0; + + // Make sure the recipient isn't on the no-refund list (for exchanges and things like that). + if (reason != 'forward_payment' && config.no_refund && config.no_refund.indexOf(to) >= 0) { + utils.log("Payment not sent to: @" + to + " for: " + reason + ' because they are on the no_refund list.'); + return; + } + + // Replace variables in the memo text + var memo = config.transfer_memos[reason]; + memo = memo.replace(/{amount}/g, utils.format(amount, 3) + ' ' + currency); + memo = memo.replace(/{currency}/g, currency); + memo = memo.replace(/{account}/g, config.account); + memo = memo.replace(/{to}/g, to); + memo = memo.replace(/{tag}/g, data); + + // Issue the payment. + steem.broadcast.transfer(config.active_key, config.account, to, utils.format(amount, 3) + ' ' + currency, memo, function (err, response) { + if (err) { + utils.log('Error sending payment to @' + to + ' for: ' + amount + ' ' + currency + ', Error: ' + err); + + // Try again on error + if(retries < 2) + setTimeout(function() { refund(to, amount, currency, reason, retries + 1, data) }, (Math.floor(Math.random() * 10) + 3) * 1000); + else + utils.log('============= Payment failed three times for: @' + to + ' ==============='); + } else { + utils.log('Payment of ' + amount + ' ' + currency + ' sent to @' + to + ' for reason: ' + reason); + } + }); +} + +function claimRewards() { + if (!config.auto_claim_rewards) + return; + + // Make api call only if you have actual reward + if (parseFloat(account.reward_steem_balance) > 0 || parseFloat(account.reward_sbd_balance) > 0 || parseFloat(account.reward_vesting_balance) > 0) { + steem.broadcast.claimRewardBalance(config.posting_key, config.account, account.reward_steem_balance, account.reward_sbd_balance, account.reward_vesting_balance, function (err, result) { + if (err) { + utils.log(err); + } + + if (result) { + + var rewards_message = "$$$ ==> Rewards Claim"; + if (parseFloat(account.reward_sbd_balance) > 0) { rewards_message = rewards_message + ' SBD: ' + parseFloat(account.reward_sbd_balance); } + if (parseFloat(account.reward_steem_balance) > 0) { rewards_message = rewards_message + ' STEEM: ' + parseFloat(account.reward_steem_balance); } + if (parseFloat(account.reward_vesting_balance) > 0) { rewards_message = rewards_message + ' VESTS: ' + parseFloat(account.reward_vesting_balance); } + + utils.log(rewards_message); + + // If there are liquid post rewards, withdraw them to the specified account + if (parseFloat(account.reward_sbd_balance) > 0 && config.post_rewards_withdrawal_account && config.post_rewards_withdrawal_account != '') { + + // Send liquid post rewards to the specified account + steem.broadcast.transfer(config.active_key, config.account, config.post_rewards_withdrawal_account, account.reward_sbd_balance, 'Liquid Post Rewards Withdrawal', function (err, response) { + if (err) + utils.log(err, response); + else { + utils.log('$$$ Auto withdrawal - liquid post rewards: ' + account.reward_sbd_balance + ' sent to @' + config.post_rewards_withdrawal_account); + } + }); + } + } + }); + } +} diff --git a/utils.js b/utils.js index 88fdc78..2835ebc 100644 --- a/utils.js +++ b/utils.js @@ -1,287 +1,290 @@ -var fs = require("fs"); -const steem = require('steem'); -var _ = require('lodash'); -const axios = require('axios'); -var config; - -steem.api.setOptions({ url: 'https://api.steemit.com' }); - -var STEEMIT_100_PERCENT = 10000; -var STEEMIT_VOTE_REGENERATION_SECONDS = (5 * 60 * 60 * 24); -var HOURS = 60 * 60; - - var steemPrice; - var rewardBalance; - var recentClaims; - var currentUserAccount; - var votePowerReserveRate; - var totalVestingFund; - var totalVestingShares; - var botNames; - function updateSteemVariables() { - steem.api.getRewardFund("post", function (e, t) { - console.log(e,t); - rewardBalance = parseFloat(t.reward_balance.replace(" STEEM", "")); - recentClaims = t.recent_claims; - }); - steem.api.getCurrentMedianHistoryPrice(function (e, t) { - steemPrice = parseFloat(t.base.replace(" SBD", "")) / parseFloat(t.quote.replace(" STEEM", "")); - }); - steem.api.getDynamicGlobalProperties(function (e, t) { - votePowerReserveRate = t.vote_power_reserve_rate; - totalVestingFund = parseFloat(t.total_vesting_fund_steem.replace(" STEEM", "")); - totalVestingShares = parseFloat(t.total_vesting_shares.replace(" VESTS", "")); - }); - - setTimeout(updateSteemVariables, 180 * 1000) - } - // updateSteemVariables(); - - function getVotingPower(account) { - var voting_power = account.voting_power; - var last_vote_time = new Date((account.last_vote_time) + 'Z'); - var elapsed_seconds = (new Date() - last_vote_time) / 1000; - var regenerated_power = Math.round((STEEMIT_100_PERCENT * elapsed_seconds) / STEEMIT_VOTE_REGENERATION_SECONDS); - var current_power = Math.min(voting_power + regenerated_power, STEEMIT_100_PERCENT); - return current_power; - } - - function getVoteRShares(voteWeight, account, power) { - if (!account) { - return; - } - - if (rewardBalance && recentClaims && steemPrice && votePowerReserveRate) { - - var effective_vesting_shares = Math.round(getVestingShares(account) * 1000000); - var voting_power = account.voting_power; - var weight = voteWeight * 100; - var last_vote_time = new Date((account.last_vote_time) + 'Z'); - - - var elapsed_seconds = (new Date() - last_vote_time) / 1000; - var regenerated_power = Math.round((STEEMIT_100_PERCENT * elapsed_seconds) / STEEMIT_VOTE_REGENERATION_SECONDS); - var current_power = power || Math.min(voting_power + regenerated_power, STEEMIT_100_PERCENT); - var max_vote_denom = votePowerReserveRate * STEEMIT_VOTE_REGENERATION_SECONDS / (60 * 60 * 24); - var used_power = Math.round((current_power * weight) / STEEMIT_100_PERCENT); - used_power = Math.round((used_power + max_vote_denom - 1) / max_vote_denom); - - var rshares = Math.round((effective_vesting_shares * used_power) / (STEEMIT_100_PERCENT)) - - return rshares; - - } - } - - function getVoteValue(voteWeight, account, power) { - if (!account) { - return; - } - if (rewardBalance && recentClaims && steemPrice && votePowerReserveRate) { - var voteValue = getVoteRShares(voteWeight, account, power) - * rewardBalance / recentClaims - * steemPrice; - - return voteValue; - - } - } - -function timeTilFullPower(cur_power){ - return (STEEMIT_100_PERCENT - cur_power) * STEEMIT_VOTE_REGENERATION_SECONDS / STEEMIT_100_PERCENT; - } - - function getVestingShares(account) { - var effective_vesting_shares = parseFloat(account.vesting_shares.replace(" VESTS", "")) - + parseFloat(account.received_vesting_shares.replace(" VESTS", "")) - - parseFloat(account.delegated_vesting_shares.replace(" VESTS", "")); - return effective_vesting_shares; - } - - function getCurrency(amount) { - return amount.substr(amount.indexOf(' ') + 1); - } - - function loadUserList(location, callback) { - if(!location) { - if(callback) - callback(null); - - return; - } - - if (location.startsWith('http://') || location.startsWith('https://')) { - // Require the "request" library for making HTTP requests - var request = require("request"); - - request.get(location, function (e, r, data) { - try { - if(callback) - callback(data.replace(/[\r]/g, '').split('\n')); - } catch (err) { - utils.log('Error loading blacklist from: ' + location + ', Error: ' + err); - - if(callback) - callback(null); - } - }); - } else if (fs.existsSync(location)) { - if(callback) - callback(fs.readFileSync(location, "utf8").replace(/[\r]/g, '').split('\n')); - } else if(callback) - callback([]); -} - -function format(n, c, d, t) { - var c = isNaN(c = Math.abs(c)) ? 2 : c, - d = d == undefined ? "." : d, - t = t == undefined ? "," : t, - s = n < 0 ? "-" : "", - i = String(parseInt(n = Math.abs(Number(n) || 0).toFixed(c))), - j = (j = i.length) > 3 ? j % 3 : 0; - return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); - } - - function toTimer(ts) { - var h = Math.floor(ts / HOURS); - var m = Math.floor((ts % HOURS) / 60); - var s = Math.floor((ts % 60)); - return padLeft(h, 2) + ':' + padLeft(m, 2) + ':' + padLeft(s, 2); - } - - function padLeft(v, d) { - var l = (v + '').length; - if (l >= d) return v + ''; - for(var i = l; i < d; i++) - v = '0' + v; - return v; - } - - async function loadBots() { - var query = await axios.get('https://steembottracker.net/bid_bots'); - var bidBots = query.data; - var query = await axios.get('https://steembottracker.net/other_bots'); - var otherBots = query.data; - // console.log(bidBots); - // console.log(otherBots); - var allBots = bidBots.concat(otherBots); - botNames = _.map(allBots, 'name'); - // console.log(botNames); - return botNames; - } - - function calculateVotes(posts, weight) { - if(typeof weight == 'undefined') { - weight = 10000; - } - var data = {}; - var x = 0; - // Rate multiplier post count - var rmc = _.countBy(posts, 'rate_multiplier'); - console.log(rmc); - _.forEach(rmc, function(value, key) { - x += key * value; - }); - console.log(x); - data.power_per_vote = Math.floor(weight / x); - return data - } - - function filterPosts(posts) { - var results = Array(); - let config = getConfig(); - for(var i = 0; i < posts.length; i++) { - var post = posts[i]; - - // Check if post category is main tag - if (post.category != config.main_tag) { - console.log('Post does not match category tag. ' + post.url); - continue; - } - //check if account was voted - let voted = _.findIndex(post.active_votes, ['voter', config.account]); - if (voted == -1) { - console.log('Post was not voted. ' + post.url); - continue; - } - // Check if account is beneficiary - var benefit = checkBeneficiary(post); - - if(!benefit) - continue; - - for (var n = 0; n < config.banned_users.length; n++) { - if (post.author === config.banned_users[n]){ - console.log('User '+post.author+' is banned, skipping his post:' + post.url); - continue; - } - } - - results.push(post); - } - return results; - - } - - function checkBeneficiary(post) { - let config = getConfig(); - // Check if account is beneficiary - var benefit = 0; - for (var x = 0; x < post.beneficiaries.length; x++) { - for (var n = 0; n < config.beneficiaries.length; n++) { - if (post.beneficiaries[x].account === config.beneficiaries[n]) - benefit ++; - } - if (benefit === config.beneficiaries.length) { - benefit = true; - break; - } - } - if(!benefit) - return false; - - return true; - - } - - function log(msg, name) { - if (!name) - var name = 'log'; - console.log(new Date().toString() + ' - ' + msg); - fs.appendFileSync( name + '.log', new Date().toString() + ' - ' + msg + "\n"); - } - - function getConfig() { - if (config) - return config; - else { - console.log('I get config'); - config = JSON.parse(fs.readFileSync("config.json")); - return config; - } - } - - async function asyncForEach(array, callback) { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array) - } -} - - - module.exports = { - getVotingPower: getVotingPower, - getVoteValue: getVoteValue, - timeTilFullPower: timeTilFullPower, - getVestingShares: getVestingShares, - loadUserList: loadUserList, - getCurrency: getCurrency, - format: format, - toTimer: toTimer, - log: log, - calculateVotes: calculateVotes, - filterPosts: filterPosts, - getConfig: getConfig, - loadBots: loadBots, - checkBeneficiary: checkBeneficiary, - asyncForEach: asyncForEach - } +var fs = require("fs"); +const steem = require('steem'); +var _ = require('lodash'); +const axios = require('axios'); +var config; + +steem.api.setOptions({ url: 'https://api.steemit.com' }); + +var STEEMIT_100_PERCENT = 10000; +var STEEMIT_VOTE_REGENERATION_SECONDS = (5 * 60 * 60 * 24); +var HOURS = 60 * 60; + + var steemPrice; + var rewardBalance; + var recentClaims; + var currentUserAccount; + var votePowerReserveRate; + var totalVestingFund; + var totalVestingShares; + var botNames; + function updateSteemVariables() { + steem.api.getRewardFund("post", function (e, t) { + console.log(e,t); + rewardBalance = parseFloat(t.reward_balance.replace(" STEEM", "")); + recentClaims = t.recent_claims; + }); + steem.api.getCurrentMedianHistoryPrice(function (e, t) { + steemPrice = parseFloat(t.base.replace(" SBD", "")) / parseFloat(t.quote.replace(" STEEM", "")); + }); + steem.api.getDynamicGlobalProperties(function (e, t) { + votePowerReserveRate = t.vote_power_reserve_rate; + totalVestingFund = parseFloat(t.total_vesting_fund_steem.replace(" STEEM", "")); + totalVestingShares = parseFloat(t.total_vesting_shares.replace(" VESTS", "")); + }); + + setTimeout(updateSteemVariables, 180 * 1000) + } + // updateSteemVariables(); + + function getVotingPower(account) { + var voting_power = account.voting_power; + var last_vote_time = new Date((account.last_vote_time) + 'Z'); + var elapsed_seconds = (new Date() - last_vote_time) / 1000; + var regenerated_power = Math.round((STEEMIT_100_PERCENT * elapsed_seconds) / STEEMIT_VOTE_REGENERATION_SECONDS); + var current_power = Math.min(voting_power + regenerated_power, STEEMIT_100_PERCENT); + return current_power; + } + + function getVoteRShares(voteWeight, account, power) { + if (!account) { + return; + } + + if (rewardBalance && recentClaims && steemPrice && votePowerReserveRate) { + + var effective_vesting_shares = Math.round(getVestingShares(account) * 1000000); + var voting_power = account.voting_power; + var weight = voteWeight * 100; + var last_vote_time = new Date((account.last_vote_time) + 'Z'); + + + var elapsed_seconds = (new Date() - last_vote_time) / 1000; + var regenerated_power = Math.round((STEEMIT_100_PERCENT * elapsed_seconds) / STEEMIT_VOTE_REGENERATION_SECONDS); + var current_power = power || Math.min(voting_power + regenerated_power, STEEMIT_100_PERCENT); + var max_vote_denom = votePowerReserveRate * STEEMIT_VOTE_REGENERATION_SECONDS / (60 * 60 * 24); + var used_power = Math.round((current_power * weight) / STEEMIT_100_PERCENT); + used_power = Math.round((used_power + max_vote_denom - 1) / max_vote_denom); + + var rshares = Math.round((effective_vesting_shares * used_power) / (STEEMIT_100_PERCENT)) + + return rshares; + + } + } + + function getVoteValue(voteWeight, account, power) { + if (!account) { + return; + } + if (rewardBalance && recentClaims && steemPrice && votePowerReserveRate) { + var voteValue = getVoteRShares(voteWeight, account, power) + * rewardBalance / recentClaims + * steemPrice; + + return voteValue; + + } + } + +function timeTilFullPower(cur_power){ + return (STEEMIT_100_PERCENT - cur_power) * STEEMIT_VOTE_REGENERATION_SECONDS / STEEMIT_100_PERCENT; + } + + function getVestingShares(account) { + var effective_vesting_shares = parseFloat(account.vesting_shares.replace(" VESTS", "")) + + parseFloat(account.received_vesting_shares.replace(" VESTS", "")) + - parseFloat(account.delegated_vesting_shares.replace(" VESTS", "")); + return effective_vesting_shares; + } + + function getCurrency(amount) { + return amount.substr(amount.indexOf(' ') + 1); + } + + function loadUserList(location, callback) { + if(!location) { + if(callback) + callback(null); + + return; + } + + if (location.startsWith('http://') || location.startsWith('https://')) { + // Require the "request" library for making HTTP requests + var request = require("request"); + + request.get(location, function (e, r, data) { + try { + if(callback) + callback(data.replace(/[\r]/g, '').split('\n')); + } catch (err) { + utils.log('Error loading blacklist from: ' + location + ', Error: ' + err); + + if(callback) + callback(null); + } + }); + } else if (fs.existsSync(location)) { + if(callback) + callback(fs.readFileSync(location, "utf8").replace(/[\r]/g, '').split('\n')); + } else if(callback) + callback([]); +} + +function format(n, c, d, t) { + var c = isNaN(c = Math.abs(c)) ? 2 : c, + d = d == undefined ? "." : d, + t = t == undefined ? "," : t, + s = n < 0 ? "-" : "", + i = String(parseInt(n = Math.abs(Number(n) || 0).toFixed(c))), + j = (j = i.length) > 3 ? j % 3 : 0; + return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); + } + + function toTimer(ts) { + var h = Math.floor(ts / HOURS); + var m = Math.floor((ts % HOURS) / 60); + var s = Math.floor((ts % 60)); + return padLeft(h, 2) + ':' + padLeft(m, 2) + ':' + padLeft(s, 2); + } + + function padLeft(v, d) { + var l = (v + '').length; + if (l >= d) return v + ''; + for(var i = l; i < d; i++) + v = '0' + v; + return v; + } + + async function loadBots() { + var query = await axios.get('https://steembottracker.net/bid_bots'); + var bidBots = query.data; + var query = await axios.get('https://steembottracker.net/other_bots'); + var otherBots = query.data; + // console.log(bidBots); + // console.log(otherBots); + var allBots = bidBots.concat(otherBots); + botNames = _.map(allBots, 'name'); + // console.log(botNames); + return botNames; + } + + // the weight param is actually 100*1,000 at max to consume 20% VP + // with 100 being the max 100% per single vote, and 1,000 being the max potentially used votes + // so if we were to only consume 10 % of our VP, the weight would be set at 50,000 instead of default value of 100,000 + function calculateVotes(posts, weight) { + if(typeof weight == 'undefined') { + weight = 100000; + } + var data = {}; + var x = 0; + // Rate multiplier post count + var rmc = _.countBy(posts, 'rate_multiplier'); + console.log(rmc); + _.forEach(rmc, function(value, key) { + x += key * value; + }); + console.log(x); + data.power_per_vote = Math.floor(weight / x); + return data + } + + function filterPosts(posts) { + var results = Array(); + let config = getConfig(); + for(var i = 0; i < posts.length; i++) { + var post = posts[i]; + + // Check if post category is main tag + if (post.category != config.main_tag) { + console.log('Post does not match category tag. ' + post.url); + continue; + } + //check if account was voted + let voted = _.findIndex(post.active_votes, ['voter', config.account]); + if (voted == -1) { + console.log('Post was not voted. ' + post.url); + continue; + } + // Check if account is beneficiary + var benefit = checkBeneficiary(post); + + if(!benefit) + continue; + + for (var n = 0; n < config.banned_users.length; n++) { + if (post.author === config.banned_users[n]){ + console.log('User '+post.author+' is banned, skipping his post:' + post.url); + continue; + } + } + + results.push(post); + } + return results; + + } + + function checkBeneficiary(post) { + let config = getConfig(); + // Check if account is beneficiary + var benefit = 0; + for (var x = 0; x < post.beneficiaries.length; x++) { + for (var n = 0; n < config.beneficiaries.length; n++) { + if (post.beneficiaries[x].account === config.beneficiaries[n]) + benefit ++; + } + if (benefit === config.beneficiaries.length) { + benefit = true; + break; + } + } + if(!benefit) + return false; + + return true; + + } + + function log(msg, name) { + if (!name) + var name = 'log'; + console.log(new Date().toString() + ' - ' + msg); + fs.appendFileSync( name + '.log', new Date().toString() + ' - ' + msg + "\n"); + } + + function getConfig() { + if (config) + return config; + else { + console.log('I get config'); + config = JSON.parse(fs.readFileSync("config.json")); + return config; + } + } + + async function asyncForEach(array, callback) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array) + } +} + + + module.exports = { + getVotingPower: getVotingPower, + getVoteValue: getVoteValue, + timeTilFullPower: timeTilFullPower, + getVestingShares: getVestingShares, + loadUserList: loadUserList, + getCurrency: getCurrency, + format: format, + toTimer: toTimer, + log: log, + calculateVotes: calculateVotes, + filterPosts: filterPosts, + getConfig: getConfig, + loadBots: loadBots, + checkBeneficiary: checkBeneficiary, + asyncForEach: asyncForEach + } From 9ae2855bee624dab3b3fe735912ed3e2f2a4708f Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 20 Aug 2018 16:16:19 +0300 Subject: [PATCH 027/193] Banned Users Issue Fix Implementing a fix for banned users to be properly excluded from reward/upvote --- curation-bot.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 0dd0742..89e5d69 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -206,14 +206,15 @@ function processVotes(query, subsequent) { } //check if user is banned - + var user_banned = false; for (var n = 0; n < config.banned_users.length; n++) { - if (post.author === config.banned_users[n]){ + if (post.author == config.banned_users[n]){ utils.log('User '+post.author+' is banned, skipping his post:' + post.url); - continue; + user_banned = true; + break; } } - + if (user_banned) continue; //skip any posts that are more than 1.5 days old if((new Date() - new Date(post.created + 'Z')) >= (1.5 * 24 * 60 * 60 * 1000)) { From b4e588d3ab6050a16b4b81f9446fe392e68fb1b1 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 20 Aug 2018 16:23:42 +0300 Subject: [PATCH 028/193] Switch process to daily rewards + relevant fixes - Parametrization of days param, and switching from weekly rewards to daily rewards, to run at 8:00 AM UTC - Steem rewards to remain on weekly basis every Monday - Fix for proper sorting of delegation transactions and storing recent ones only - Cleanup/refactoring for delegation process. --- delegations.js | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/delegations.js b/delegations.js index c1437a6..059f265 100644 --- a/delegations.js +++ b/delegations.js @@ -2,7 +2,6 @@ const dsteem = require('dsteem') const client = new dsteem.Client('https://api.steemit.com') const _ = require('lodash') const moment = require('moment') -var schedule = require('node-schedule') const utils = require('./utils') const mail = require('./mail') @@ -20,6 +19,18 @@ let properties let totalVests let totalSteem +console.log('--- Reward script initialized ---'); + +var schedule = require('node-schedule') +//console.log('pre-schedule'); +var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ + console.log('--- Start delegators reward ---'); + runRewards(); +}); + +runRewards(); + +function runRewards(){ // Use connect method to connect to the server MongoClient.connect(config.mongo_uri, async function (err, dbClient) { if (!err) { @@ -28,14 +39,11 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { db = dbClient.db(dbName) // Get the documents collection collection = db.collection(collectionName) - //startProcess() - // schedule to run every monday at 00:00 - let jobd = schedule.scheduleJob('0 0 * * 1', startProcess) - // processTokenRewards() - // processSteemRewards('2018-07-23') - // let rewards = await getAcumulatedRewards('2018-07-09', '2018-07-16') - // console.log(rewards) - // getBenefactorRewards('actifit.pay') + + //run for one day + var days = 1; + startProcess(days); + } else { utils.log(err, 'delegations') mail.sendPlainMail('Database Error', err, config.report_emails) @@ -49,21 +57,28 @@ MongoClient.connect(config.mongo_uri, async function (err, dbClient) { process.exit() } }) +} -async function startProcess () { +async function startProcess (days) { let end = 0 // Find last saved delegation transaction - let lastTx = await collection.find().sort({'tx_date': -1}).limit(1).next() + let lastTx = await collection.find().sort({'tx_number': -1}).limit(1).next() + console.log(lastTx) if (lastTx) end = lastTx.tx_number await updateProperties() await processDelegations(config.account, -1, end) - let start = moment().utc().startOf('date').subtract(7, 'days').toDate() + let start = moment().utc().startOf('date').subtract(days, 'days').toDate() let txEnd = moment().utc().startOf('date').toDate() - processTokenRewards(start, txEnd) + //await processTokenRewards(start, txEnd, days) + var d = new Date(); + var dayId = d.getDay(); + // Check if today is Monday, to calculate steem rewards + if (dayId == 1){ processSteemRewards(txEnd) } +} -async function processTokenRewards (start, end) { +async function processTokenRewards (start, end, days) { if (!start) start = moment().utc().startOf('date').subtract(7, 'days').toDate() if (!end) end = moment().utc().startOf('date').toDate() let note = 'Delegation Reward Until EOD ' + moment(end).format('MMMM Do YYYY') From cf12070359666cad93f6528d9aa6a533ab1e5906 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 20 Aug 2018 16:28:51 +0300 Subject: [PATCH 029/193] SP Calculation Changes - Creating new query/function to grab SP up to any given date. - Fix for proper calculation of token amount to be awarded, as prior multiplier and SP calculation was inaccurate - Storing steem rewards on the file system instead of mailing them --- delegations.js | 177 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 119 insertions(+), 58 deletions(-) diff --git a/delegations.js b/delegations.js index 059f265..fea14e1 100644 --- a/delegations.js +++ b/delegations.js @@ -31,74 +31,85 @@ var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ runRewards(); function runRewards(){ -// Use connect method to connect to the server -MongoClient.connect(config.mongo_uri, async function (err, dbClient) { - if (!err) { - console.log('Connected successfully to server: ' + config.mongo_uri) + // Use connect method to connect to the server + MongoClient.connect(config.mongo_uri, async function (err, dbClient) { + if (!err) { + console.log('Connected successfully to server: ' + config.mongo_uri) - db = dbClient.db(dbName) - // Get the documents collection - collection = db.collection(collectionName) + db = dbClient.db(dbName) + // Get the documents collection + collection = db.collection(collectionName) //run for one day var days = 1; startProcess(days); - } else { - utils.log(err, 'delegations') - mail.sendPlainMail('Database Error', err, config.report_emails) - .then(function (res, err) { - if (!err) { - console.log(res) - } else { - utils.log(err, 'import') - } - }) - process.exit() - } -}) + + } else { + utils.log(err, 'delegations') + mail.sendPlainMail('Database Error', err, config.report_emails) + .then(function (res, err) { + if (!err) { + console.log(res) + } else { + utils.log(err, 'import') + } + }) + process.exit() + } + }) } async function startProcess (days) { - let end = 0 - // Find last saved delegation transaction + let end = 0 + // Find last saved delegation transaction let lastTx = await collection.find().sort({'tx_number': -1}).limit(1).next() console.log(lastTx) - if (lastTx) end = lastTx.tx_number - await updateProperties() - await processDelegations(config.account, -1, end) + if (lastTx) end = lastTx.tx_number + await updateProperties() + await processDelegations(config.account, -1, end) let start = moment().utc().startOf('date').subtract(days, 'days').toDate() - let txEnd = moment().utc().startOf('date').toDate() + let txEnd = moment().utc().startOf('date').toDate() //await processTokenRewards(start, txEnd, days) var d = new Date(); var dayId = d.getDay(); // Check if today is Monday, to calculate steem rewards if (dayId == 1){ - processSteemRewards(txEnd) -} + processSteemRewards(txEnd) + } } async function processTokenRewards (start, end, days) { - if (!start) start = moment().utc().startOf('date').subtract(7, 'days').toDate() - if (!end) end = moment().utc().startOf('date').toDate() - let note = 'Delegation Reward Until EOD ' + moment(end).format('MMMM Do YYYY') - let acumulatedSteemPower = await getAcumulatedSteemPower(start, end) - let multiplier = 1 - console.log(acumulatedSteemPower) - if (acumulatedSteemPower > config.weekly_rewards_limit) { - multiplier = acumulatedSteemPower / config.weekly_rewards_limit - } - for (let user of acumulatedSteemPower.users) { - let reward = { - user: user.user, - token_count: +(user.totalSteem * multiplier).toFixed(3), - reward_activity: 'Delegation', - note: note, - date: end - } - console.log(reward) - upsertRewardTransaction(reward) - } + if (!start) start = moment().utc().startOf('date').subtract(days, 'days').toDate() + if (!end) end = moment().utc().startOf('date').toDate() + let note = 'Delegation Reward For ' + moment(end).subtract(1, 'days').format('MMMM Do YYYY') + + let acumulatedSteemPower = await getAcumulatedSteemPower(start, end) + + //handles maintaining max CAP for payments + let multiplier = 1 + + let currentSteemPower = await getCurrentTotalSP(end); + console.log("currentSteemPower:"+currentSteemPower); + + //check if max CAP is reached, and apply multplier accordingly + if (currentSteemPower > config.weekly_rewards_limit) { + multiplier = config.weekly_rewards_limit / currentSteemPower; + console.log(">>>>went beyond rewards limit. Apply multiplier"); + } + console.log(">>>>multiplier:"+multiplier); + //go through all delegators, and send out AFIT rewards + for (let user of acumulatedSteemPower.users) { + let reward = { + user: user.user, + token_count: parseFloat((user.totalSteem * multiplier).toFixed(3)), + reward_activity: 'Delegation', + note: note, + date: end + } + console.log(reward) + upsertRewardTransaction(reward) + } } async function processSteemRewards (start) { @@ -117,25 +128,35 @@ async function processSteemRewards (start) { user: o.user, steem: +(o.totalSteem * rewardPerSteem).toFixed(3) } - let url = 'https://v2.steemconnect.com/sign/transfer?from=[PAY_ACCOUNT]&to=[TO_ACCOUNT]&amount=[AMOUNT]%20STEEM&memo=Delegation%20Rewards' + /*let url = 'https://v2.steemconnect.com/sign/transfer?from=[PAY_ACCOUNT]&to=[TO_ACCOUNT]&amount=[AMOUNT]%20STEEM&memo=Delegation%20Rewards' url = url.replace('[PAY_ACCOUNT]', config.pay_account) url = url.replace('[TO_ACCOUNT]', reward.user) url = url.replace('[AMOUNT]', reward.steem) - reward.url = url + reward.url = url*/ return reward }) console.log(rewards) - console.log(steemRewards) + console.log("steem total beneficiary reward:"+steemRewards) const data = { rewards: rewards, total: steemRewards, totalUsers: rewards.length } + + var fs = require('fs'); + fs.writeFile("steemrewards.json", JSON.stringify(rewards), function(err) { + if(err) { + return console.log(err); + } + + console.log("The file was saved!"); + }); + /* const attachment = { filename: 'rewards.json', content: JSON.stringify(rewards) } - mail.sendWithTemplate('Rewards mail', data, config.report_emails, 'rewards', attachment) + mail.sendWithTemplate('Rewards mail', data, config.report_emails, 'rewards', attachment)*/ }) } @@ -172,19 +193,23 @@ async function processDelegations (account, start, end) { // console.log(delegationTransactions) if (delegationTransactions.length > 0) { await collection.insert(delegationTransactions) - updateActiveDelegations() + await updateActiveDelegations() } else { console.log('--- No new delegations ---') - return + return; } // If more pending delegations call process againg with new index - if (start !== limit && !ended) return processDelegations(account, lastTrans, end) + if (start !== limit && !ended){ + return processDelegations(account, lastTrans, end) + } // console.log(transactions) - return + return; } catch (err) { console.log(err) // Consider exponential backoff if extreme cases start happening - if (err.type === 'request-timeout' || err.type === 'body-timeout') return processDelegations(account, start, end) + if (err.type === 'request-timeout' || err.type === 'body-timeout'){ + return processDelegations(account, start, end); + } } } @@ -252,6 +277,40 @@ async function getActiveDelegations (start) { ).toArray() } +/* + * function handles grabbing the total current SP value before a specific date + * params: toDate - date before which all current SP is calculated + * returns: total value of current SP count up to passed date + */ +async function getCurrentTotalSP(toDate){ + toDate = moment(toDate).toDate() + + var actDelgCol = 'active_delegations'; + //perform an aggregation based on max date, exluded delegators, and return back sum of SP and delegator count (we only need for now totalSP) + var results = await db.collection(actDelgCol).aggregate([ + { + $match: + { + 'tx_date': {$lt: toDate}, + 'delegator': {$nin: config.exclude_rewards} + } + }, + { + $group: + { + _id: null, + totalSP: { $sum: "$steem_power" }, + totalDelegators: { $sum: 1 } + } + } + ]).toArray(); + //function(err, results) { + //var output = 'tokens distributed:'+results[0].totalSP; + console.log(results); + return results[0].totalSP; + //}); +} + async function getAcumulatedSteemPower (from, to) { let result = { users: [] @@ -310,7 +369,9 @@ async function updateActiveDelegations () { ) let activeDelegations = await query.toArray() await db.collection('active_delegations').drop() - return db.collection('active_delegations').insert(activeDelegations) + await db.collection('active_delegations').insert(activeDelegations) + console.log('done updating delegations'); + return ; } function upsertRewardTransaction (reward) { From 8e911cf9c22f163ec5d4a13641f024aa3de50605 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 20 Aug 2018 16:57:01 +0300 Subject: [PATCH 030/193] Save Process Fix Fix for token save process becoming unresponsive after error --- save-data.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/save-data.js b/save-data.js index a495456..f6ddff7 100644 --- a/save-data.js +++ b/save-data.js @@ -127,8 +127,11 @@ function getPosts(index) { console.log(err); return; } + });*/ + }).finally(function() { + //making sure we don't get caught up in infinite loop after some error + postsProcessing = false; }); - }) } else { utils.log(err, 'import'); mail.sendPlainMail('0 posts...', err, config.report_emails) From 94bf0ec875c04b8fa2d3ebd02ad732bbb355407c Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Tue, 25 Sep 2018 18:58:36 +0300 Subject: [PATCH 031/193] Add several API functions Created a multitude of API functions to allow further data to be fetched relevant to Actifit: - delegation payments on a specific date - top delegators list sorted by delegated amount - user's last recorded delegation amount - current actifit moderators - current actifit ambassadors - top AFIT token holders - Actifit banned users - number of reblogs, upvotes, and rewarded posts on a specific day (each a separate API call) --- app.js | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 5426ede..eb7048a 100644 --- a/app.js +++ b/app.js @@ -80,7 +80,7 @@ app.get('/transactions/:user?', async function (req, res) { }); /* end point for returning number of awarded users and tokens distributed */ -app.get('/user-tokens-info', async function(req, res) { +app.get('/userTokensInfo', async function(req, res) { await db.collection(collection_name).aggregate([ { @@ -104,6 +104,42 @@ app.get('/user-tokens-info', async function(req, res) { }); + +/* end point for returning total delegation payments (number of delegators and amount paid) on a specific date */ +app.get('/delegationPayments', async function(req, res) { + var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + var dateRegex = todayDate; // /^2018-08-05/ + if (req.query.targetDate){ + dateRegex = req.query.targetDate; + } + + await db.collection('token_transactions').aggregate([ + { + $match: + { + "reward_activity": "Delegation", + "date": { + '$eq' : new Date(dateRegex) + } + } + }, + { + $group: + { + _id: null, + tokens_distributed: { $sum: "$token_count" }, + user_count: { $sum: 1 } + } + } + ]).toArray(function(err, results) { + res.header('Access-Control-Allow-Origin', '*'); + res.send(results); + console.log(results); + }); + +}); + + /* end point for returning count of posts/activities rewarded */ app.get('/rewarded-activity-count', async function(req, res) { @@ -124,4 +160,130 @@ app.get('/charities', async function (req, res) { res.send(charities); }); -app.listen(process.env.PORT || 3000); \ No newline at end of file +/* end point for returning current active delegator data by actifit */ +app.get('/topDelegators', async function (req, res) { + var delegatorList; + if (isNaN(req.query.count)){ + delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).toArray(); + }else{ + delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).limit(parseInt(req.query.count)).toArray(); + } + res.header('Access-Control-Allow-Origin', '*'); + res.send(delegatorList); +}); + +/* end point for returning a single user last recorded delegation amount */ +app.get('/delegation/:user', async function (req, res) { + let user = await db.collection('active_delegations').findOne({_id: req.params.user}, {fields : { _id:0} }); + console.log(user); + res.header('Access-Control-Allow-Origin', '*'); + res.send(user); +}); + +/* end point for returning current active moderators data by actifit */ +app.get('/moderators', async function (req, res) { + var moderatorList; + moderatorList = await db.collection('team').find({title:'moderator', status:'active'}).sort({name: 1}).toArray(); + res.header('Access-Control-Allow-Origin', '*'); + res.send(moderatorList); +}); + +/* end point for returning current active ambassadors data by actifit */ +app.get('/ambassadors', async function (req, res) { + var ambassadorList; + ambassadorList = await db.collection('team').find({title:'ambassador', status:'active'}).sort({name: 1}).toArray(); + res.header('Access-Control-Allow-Origin', '*'); + res.send(ambassadorList); +}); + +/* end point for returning current top AFIT token holders */ +app.get('/topTokenHolders', async function (req, res) { + var delegatorList; + if (isNaN(req.query.count)){ + delegatorList = await db.collection('user_tokens').find().sort({tokens: -1}).toArray(); + }else{ + delegatorList = await db.collection('user_tokens').find().sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); + } + res.header('Access-Control-Allow-Origin', '*'); + res.send(delegatorList); +}); + + +/* end point for returning accounts banned by actifit*/ +app.get('/bannedUsers', async function (req, res) { + var banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); + res.header('Access-Control-Allow-Origin', '*'); + res.send(banned_users); +}); + + +/* end point for counting number of reblogs on a certain date param (default current date) */ +app.get('/reblogCount', async function (req, res) { + var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + //fileName = "steemrewards"+fileName+".json"; + var dateRegex = new RegExp ('^'+todayDate); // /^2018-08-05/ + if (req.query.targetDate){ + dateRegex = new RegExp ('^'+req.query.targetDate); + } + let query = await db.collection('token_transactions').find({ + "reward_activity": "Post Reblog", + "date": dateRegex + }) + try{ + console.log('counting'); + let reblog_count = await query.count(); + console.log(reblog_count); + res.header('Access-Control-Allow-Origin', '*'); + res.send(JSON.stringify({reblog_count:reblog_count})); + }catch(err){ + console.log(err.message); + } +}); + +/* end point for counting number of upvotes on a certain date param (default current date) */ +app.get('/upvoteCount', async function (req, res) { + var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + //fileName = "steemrewards"+fileName+".json"; + var dateRegex = new RegExp ('^'+todayDate); // /^2018-08-05/ + if (req.query.targetDate){ + dateRegex = new RegExp ('^'+req.query.targetDate); + } + let query = await db.collection('token_transactions').find({ + "reward_activity": "Post Vote", + "date": dateRegex + }) + try{ + console.log('counting'); + let upvote_count = await query.count(); + console.log(upvote_count); + res.header('Access-Control-Allow-Origin', '*'); + res.send(JSON.stringify({upvote_count:upvote_count})); + }catch(err){ + console.log(err.message); + } +}); + +/* end point for counting number of rewarded posts on a certain date param (default current date) */ +app.get('/rewardedPostCount', async function (req, res) { + var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + //fileName = "steemrewards"+fileName+".json"; + var dateRegex = new RegExp ('^'+todayDate); // /^2018-08-05/ + if (req.query.targetDate){ + dateRegex = new RegExp ('^'+req.query.targetDate); + } + let query = await db.collection('token_transactions').find({ + "reward_activity": "Post", + "date": dateRegex + }) + try{ + console.log('counting'); + let rewarded_post_count = await query.count(); + console.log(rewarded_post_count); + res.header('Access-Control-Allow-Origin', '*'); + res.send(JSON.stringify({rewarded_post_count:rewarded_post_count})); + }catch(err){ + console.log(err.message); + } +}); + +app.listen(process.env.PORT || 3000); From b80b1b22bb9011dd54c400362d08285ea49504ad Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 30 Sep 2018 23:33:46 +0300 Subject: [PATCH 032/193] Add User Rank Calculation - Add User Rank Calculation API, including new end points for get delegation per user, user rewarded post count, and user rank, as well as a utils function for score calculation - Refactor Older APIs --- app.js | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- utils.js | 23 +++++++ 2 files changed, 207 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index eb7048a..9ca75c3 100644 --- a/app.js +++ b/app.js @@ -2,6 +2,7 @@ var express = require('express'); var exphbs = require('express-handlebars'); const MongoClient = require('mongodb').MongoClient; var utils = require('./utils'); +const moment = require('moment') var app = express(); @@ -49,8 +50,8 @@ app.get('/', function (req, res) { res.send('Hello there!'); }); -/* end point for user total token count display */ -app.get('/user/:user', async function (req, res) { +/* function handles calculating and returning user token count */ +grabUserTokensFunc = async function (req, res){ let user = await collection.findOne({_id: req.params.user}, {fields : { _id:0} }); console.log(user); //fixing token amount display for 3 digits @@ -59,6 +60,12 @@ app.get('/user/:user', async function (req, res) { user.tokens = user.tokens.toFixed(3) } } + return user; +} + +/* end point for user total token count display */ +app.get('/user/:user', async function (req, res) { + let user = await grabUserTokensFunc(req,res); res.header('Access-Control-Allow-Origin', '*'); res.send(user); }); @@ -172,10 +179,15 @@ app.get('/topDelegators', async function (req, res) { res.send(delegatorList); }); -/* end point for returning a single user last recorded delegation amount */ -app.get('/delegation/:user', async function (req, res) { +activeDelegationFunc = async function (req, res){ let user = await db.collection('active_delegations').findOne({_id: req.params.user}, {fields : { _id:0} }); console.log(user); + return user; +} + +/* end point for returning a single user last recorded active delegation amount */ +app.get('/delegation/:user', async function (req, res) { + var user = await activeDelegationFunc(req, res); res.header('Access-Control-Allow-Origin', '*'); res.send(user); }); @@ -286,4 +298,172 @@ app.get('/rewardedPostCount', async function (req, res) { } }); +/* refactored function to grab rewarded post count per user for use across get calls */ +userRewardedPostCountFunc = async function(req, res){ + var user = req.params.user; + //default query + var query_json = { + "reward_activity": "Post", + "user": user + }; + //if this is a sum for specific period v/s a total sum + if (typeof req.query.period != "undefined" && !isNaN(req.query.period)){ + var days = req.query.period; + //console.log("days:"+days); + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + var endDate = moment(moment().utc().startOf('date').subtract(days, 'days').toDate()).format('YYYY-MM-DD'); + var startDateRegex = new RegExp ('^'+startDate); // /^2018-08-05/ + var endDateRegex = new RegExp ('^'+endDate); // /^2018-08-05/ + //console.log("startDate:"+startDate+" endDate:"+endDate); + //adjust query to include dates + query_json = { + "reward_activity": "Post", + "user": user, + "date": { + "$gte": endDate, + "$lt": startDate + } + }; + } + + //build up query accordingly + let query = await db.collection('token_transactions').find(query_json); + try{ + //grab total number of matching records + let rewarded_post_count = await query.count(); + //console.log("rewarded_post_count:"+rewarded_post_count); + + return rewarded_post_count; + }catch(err){ + console.log(err.message); + return ""; + } +} + +/* end point for counting number of rewarded posts on a certain date param (default current date) */ +app.get('/userRewardedPostCount/:user', async function (req, res) { + + //grab user account + if (typeof req.params.user!= "undefined" && req.params.user!=null){ + + var rewarded_post_count = await userRewardedPostCountFunc(req, res); + res.header('Access-Control-Allow-Origin', '*'); + res.send(JSON.stringify({rewarded_post_count:rewarded_post_count})); + }else{ + res.send(""); + } +}); + +/* end point for getting current user's Actifit rank */ +app.get('/getRank/:user', async function (req, res) { + + if (typeof req.params.user!= "undefined" && req.params.user!=null){ + + //delegation calculation matrix + var delegation_rules = [ + [9,0], + [499,0.05], + [999,0.10], + [4999,0.20], + [9999,0.30], + [19999,0.40], + [49999,0.55], + [99999,0.65], + [499999,0.75], + [999999,0.90], + [1000000,1] + ] + + //AFIT token calculation matrix + var afit_token_rules = [ + [9,0], + [999,0.10], + [4999,0.20], + [9999,0.30], + [19999,0.40], + [49999,0.50], + [99999,0.60], + [499999,0.70], + [999999,0.80], + [4999999,0.90], + [5000000,1] + ] + + //Rewarded Posts calculation matrix + var rewarded_posts_rules = [ + [9,0], + [29,0.10], + [59,0.20], + [89,0.30], + [119,0.40], + [179,0.50], + [359,0.60], + [539,0.70], + [719,0.80], + [1079,0.90], + [1080,1] + ] + + //Rewarded Posts calculation matrix + var recent_reward_posts_rules = [ + [0,0], + [2,0.20], + [4,0.40], + [6,0.60], + [8,0.80], + [10,1] + ] + + var user_rank = 0; + + //grab delegation amount + var userDelegations = await activeDelegationFunc(req, res); + //console.log(userDelegations.steem_power); + + var delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(userDelegations.steem_power)); + + user_rank += delegation_score; + + //grab user token count + var userTokens = await grabUserTokensFunc(req,res); + //console.log(userTokens.tokens); + + var afit_tokens_score = utils.calcScore(afit_token_rules, config.afit_token_factor, parseFloat(userTokens.tokens)); + + user_rank += afit_tokens_score; + + //grab total rewarded posts count + var tot_rewarded_post_count = await userRewardedPostCountFunc(req, res); + //console.log(tot_rewarded_post_count); + + var tot_posts_score = utils.calcScore(rewarded_posts_rules, config.rewarded_posts_factor, parseInt(tot_rewarded_post_count)); + + user_rank += tot_posts_score; + + //set the check period for config value of days days, and rerun the call to get last rewarded posting activity during this period + req.query.period = config.recent_posts_period; + + var recent_rewarded_post_count = await userRewardedPostCountFunc(req, res); + //console.log(recent_rewarded_post_count); + + var recent_posts_score = utils.calcScore(recent_reward_posts_rules, config.recent_posts_factor, parseInt(recent_rewarded_post_count)); + + user_rank += recent_posts_score; + + var score_components = JSON.stringify({ + user_rank: user_rank, + delegation_score: delegation_score, + afit_tokens_score: afit_tokens_score, + tot_posts_score: tot_posts_score, + recent_posts_score:recent_posts_score + }); + console.log(score_components) + + res.header('Access-Control-Allow-Origin', '*'); + res.send(score_components); + }else{ + res.send(""); + } +}); + app.listen(process.env.PORT || 3000); diff --git a/utils.js b/utils.js index 2835ebc..04911eb 100644 --- a/utils.js +++ b/utils.js @@ -246,6 +246,28 @@ function format(n, c, d, t) { return true; } + + /** + * function handles mapping and calculating relevant score + * params: + * * 2D array providing couplets of rules + * * factor multipier for data + * * current value to compare + */ + function calcScore(rules_array, factor, value){ + var result; + for (var i=0; i Date: Sun, 30 Sep 2018 23:35:20 +0300 Subject: [PATCH 033/193] Adjust Voting Power (Mana) Calculation post HF20 --- utils.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/utils.js b/utils.js index 04911eb..5abcbd9 100644 --- a/utils.js +++ b/utils.js @@ -37,13 +37,32 @@ var HOURS = 60 * 60; } // updateSteemVariables(); - function getVotingPower(account) { + /*function getVotingPower(account) { var voting_power = account.voting_power; var last_vote_time = new Date((account.last_vote_time) + 'Z'); var elapsed_seconds = (new Date() - last_vote_time) / 1000; var regenerated_power = Math.round((STEEMIT_100_PERCENT * elapsed_seconds) / STEEMIT_VOTE_REGENERATION_SECONDS); var current_power = Math.min(voting_power + regenerated_power, STEEMIT_100_PERCENT); return current_power; + }*/ + + //fixed implementation of proper voting power calculation + function getVotingPower(account) { + const totalShares = parseFloat(account.vesting_shares) + parseFloat(account.received_vesting_shares) - parseFloat(account.delegated_vesting_shares) - parseFloat(account.vesting_withdraw_rate); + + const elapsed = Math.floor(Date.now() / 1000) - account.voting_manabar.last_update_time; + const maxMana = totalShares * 1000000; + // 432000 sec = 5 days + let currentMana = parseFloat(account.voting_manabar.current_mana) + elapsed * maxMana / 432000; + + if (currentMana > maxMana) { + currentMana = maxMana; + } + + const currentManaPerc = currentMana * 100 / maxMana; + + console.log(currentManaPerc); + return currentManaPerc; } function getVoteRShares(voteWeight, account, power) { @@ -119,7 +138,7 @@ function timeTilFullPower(cur_power){ if(callback) callback(data.replace(/[\r]/g, '').split('\n')); } catch (err) { - utils.log('Error loading blacklist from: ' + location + ', Error: ' + err); + console.log('Error loading blacklist from: ' + location + ', Error: ' + err); if(callback) callback(null); From a62366c93cd45764731cb3c851eb599eae3f4b72 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 30 Sep 2018 23:43:05 +0300 Subject: [PATCH 034/193] Dynamic Banned Users List and other changes Implement dynamic banned users list fetched from DB Adding support for encrypted values in JSON data Changing max post date to a configurable value Decreasing inter-post-comment waiting time to 3 seconds instead of 10 (HF20) Implementing JSON meta on comment level in preparation for new reward system --- curation-bot.js | 102 ++++++++++++++++++++++++++++++++++++++++++------ utils.js | 28 +++++++++++-- 2 files changed, 113 insertions(+), 17 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 89e5d69..0ba941b 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -4,6 +4,7 @@ var utils = require('./utils'); var mail = require('./mail'); var _ = require('lodash'); var moment = require('moment'); +const MongoClient = require('mongodb').MongoClient; var account = null; var last_trans = 0; @@ -16,10 +17,13 @@ var last_voted = 0; var vote_time; var last_votes = Array(); var skip = false; -var version = '0.0.1'; +var version = '0.3.4'; var error_sent = false; +var crypto = require('crypto'); + steem.api.setOptions({ url: 'https://api.steemit.com' });//https://gtg.steem.house:8090 +//steem.api.setOptions({ url: 'https://gtg.steem.house:8090' }); utils.log("* START - Version: " + version + " *"); @@ -27,6 +31,18 @@ utils.log("* START - Version: " + version + " *"); loadConfig(); var botNames; + +// Connection URL +const url = config.mongo_uri; +var db; +var collection; +// Database Name +const db_name = config.db_name; +const collection_name = 'banned_accounts'; + +var banned_users; + + // Check if bot state has been saved to disk, in which case load it if (fs.existsSync('state.json')) { var state = JSON.parse(fs.readFileSync("state.json")); @@ -50,7 +66,25 @@ if (fs.existsSync('members.json')) { utils.log('Loaded ' + members.length + ' members.'); } + +// Use connect method to connect to the server +MongoClient.connect(url, function(err, client) { + if(!err) { + console.log("Connected successfully to server"); + + db = client.db(db_name); + + // Get the documents collection + collection = db.collection(collection_name); + //only start the process once we connected to the DB startProcess(); + } else { + utils.log(err, 'api'); + } + +}); + + // Schedule to run every minute setInterval(startProcess, 60 * 1000); @@ -83,18 +117,36 @@ async function startProcess() { //deactivating condition of 24 hrs to pass var passedOneDay = true;//today >= oneMoreDay; + console.log('found banned users'); + //console.log(banned_users); + + /*for (var n = 0; n < banned_users.length; n++) { + console.log(banned_users[n].user); + //if (post.author == banned_users[n].user){ + //utils.log('User '+post.author+' is banned, skipping his post:' + post.url); + //user_banned = true; + //break; + //} + } + return;*/ + if (account && !skip && !is_voting && passedOneDay) { // Load the current voting power of the account var vp = utils.getVotingPower(account); if (config.detailed_logging) - utils.log('Voting Power: ' + utils.format(vp / 100) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); + utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); - console.log('Voting Power: ' + utils.format(vp / 100) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); + console.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); // We are at voting power kick start - time to vote! - if (vp >= config.vp_kickstart) { + //console.log(vp >= parseFloat(config.vp_kickstart)/100); + if (vp >= parseFloat(config.vp_kickstart)/100) { + console.log('lets vote'); skip = true; + //grab banned user list before rewarding + banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); + var query = {tag: config.main_tag, limit: 100}; votePosts = Array(); processVotes(query, false); @@ -207,8 +259,8 @@ function processVotes(query, subsequent) { //check if user is banned var user_banned = false; - for (var n = 0; n < config.banned_users.length; n++) { - if (post.author == config.banned_users[n]){ + for (var n = 0; n < banned_users.length; n++) { + if (post.author == banned_users[n].user){ utils.log('User '+post.author+' is banned, skipping his post:' + post.url); user_banned = true; break; @@ -217,13 +269,13 @@ function processVotes(query, subsequent) { if (user_banned) continue; //skip any posts that are more than 1.5 days old - if((new Date() - new Date(post.created + 'Z')) >= (1.5 * 24 * 60 * 60 * 1000)) { + if((new Date() - new Date(post.created + 'Z')) >= (config.max_days * 24 * 60 * 60 * 1000)) { continue; } - + var step_count = -1; try { post.json = JSON.parse(post.json_metadata); - var step_count = post.json.step_count; + step_count = post.json.step_count; if (step_count < 5000) continue; else if (step_count < 6000) @@ -236,6 +288,8 @@ function processVotes(query, subsequent) { post.rate_multiplier = 0.65; else if(step_count < 10000) post.rate_multiplier = 0.8; + else if(step_count > 150000) + continue; else post.rate_multiplier = 1; } catch (err) { @@ -245,6 +299,26 @@ function processVotes(query, subsequent) { } + //check if the post has an encryption key val, and ensure it is the proper one + if (post.json.actiCrVal){ + var txt_to_encr = post.author + post.permlink + step_count ; + var cipher = crypto.createCipher(config.encr_mode, config.encr_key); + let encr_txt = cipher.update(txt_to_encr, 'utf8', 'hex'); + encr_txt += cipher.final('hex'); + //test the result to the post's relevant data + if (post.json.actiCrVal != encr_txt){ + //wrong, skip post + console.log('post has incorrect actiCrVal'); + continue; + } + //console.log('post is valid'); + }else{ + console.log('post does not contain actiCrVal'); + continue; + } + + + let last_index = _.findLastIndex(votePosts, ['author', post.author]); if (last_index != -1) { console.log('---- User already has vote ------'); @@ -324,9 +398,9 @@ function votingProcess(posts, power_per_vote) { // Get the first bid in the list sendVote(posts.pop(), 20, power_per_vote) .then( res => { - // If there are more bids, vote on the next one after 10 seconds + // If there are more posts, vote on the next one after 5 seconds if (posts.length > 0) { - setTimeout(function () { votingProcess(posts, power_per_vote); }, 10000); + setTimeout(function () { votingProcess(posts, power_per_vote); }, 3000); } else { post_rank = 0; setTimeout(function () { @@ -373,7 +447,7 @@ function sendVote(post, retries, power_per_vote) { .catch(err => { reject(err); }) - }, 10000); + }, 3000); else resolve(result); } else { @@ -422,9 +496,11 @@ function sendComment(parentAuthor, parentPermlink, vote_weight, rate_multiplier, // Replace variables in the promotion content content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{milestone\}/g, milestone_txt).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); + //adding proper meta content for later relevant reward via afit_tokens data + var jsonMetadata = { tags: ['actifit'], app: 'actifit/v'+version, afit_tokens: token_count }; // Broadcast the comment - steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, '{"app":"communitybot/' + version + '"}', function (err, result) { + steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { if (!err && result) { utils.log('Posted comment: ' + permlink); resolve(result); diff --git a/utils.js b/utils.js index 5abcbd9..371a343 100644 --- a/utils.js +++ b/utils.js @@ -18,6 +18,7 @@ var HOURS = 60 * 60; var totalVestingFund; var totalVestingShares; var botNames; + function updateSteemVariables() { steem.api.getRewardFund("post", function (e, t) { console.log(e,t); @@ -209,9 +210,13 @@ function format(n, c, d, t) { return data } - function filterPosts(posts) { + function filterPosts(posts, banned_users) { var results = Array(); let config = getConfig(); + //takes care of making sure if we reached too far back in history + var dateSurpassed = 0; + + for(var i = 0; i < posts.length; i++) { var post = posts[i]; @@ -232,15 +237,30 @@ function format(n, c, d, t) { if(!benefit) continue; - for (var n = 0; n < config.banned_users.length; n++) { - if (post.author === config.banned_users[n]){ + + //check if user is banned + var user_banned = false; + for (var n = 0; n < banned_users.length; n++) { + if (post.author == banned_users[n].user){ console.log('User '+post.author+' is banned, skipping his post:' + post.url); - continue; + user_banned = true; + break; } } + if (user_banned) continue; + + //go back only to predefined days in history + if((new Date() - new Date(post.created + 'Z')) >= (config.max_days * 24 * 60 * 60 * 1000)) { + dateSurpassed += 1; + continue; + } results.push(post); } + //if we got to old posts and received at least 10 posts, inform calling function that no need to move forward further + if (results.length == 0 && dateSurpassed>10){ + return -1; + } return results; } From a0eaa91948c45ab6bdeaea525dad8fe850cacd92 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 30 Sep 2018 23:48:44 +0300 Subject: [PATCH 035/193] Steem only rewards + Opt out users pay Adding option for Steem only rewards run mode Storing delegation payments on local file Adding option for skipping, or not, opt out users from specific reward types --- delegations.js | 112 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 14 deletions(-) diff --git a/delegations.js b/delegations.js index fea14e1..7fba4ff 100644 --- a/delegations.js +++ b/delegations.js @@ -9,6 +9,8 @@ const config = utils.getConfig() const MongoClient = require('mongodb').MongoClient +const testRun = false; + let db let collection // Database Name @@ -25,12 +27,15 @@ var schedule = require('node-schedule') //console.log('pre-schedule'); var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ console.log('--- Start delegators reward ---'); - runRewards(); + runRewards(false);//param steemOnlyReward }); -runRewards(); -function runRewards(){ + +//param steemOnlyReward +runRewards(true); + +function runRewards(steemOnlyReward){ // Use connect method to connect to the server MongoClient.connect(config.mongo_uri, async function (err, dbClient) { if (!err) { @@ -42,7 +47,7 @@ function runRewards(){ //run for one day var days = 1; - startProcess(days); + startProcess(days, steemOnlyReward); } else { @@ -60,21 +65,28 @@ function runRewards(){ }) } -async function startProcess (days) { + +async function startProcess (days, steemOnlyReward) { let end = 0 // Find last saved delegation transaction let lastTx = await collection.find().sort({'tx_number': -1}).limit(1).next() console.log(lastTx) if (lastTx) end = lastTx.tx_number await updateProperties() + if (!steemOnlyReward){ await processDelegations(config.account, -1, end) + } let start = moment().utc().startOf('date').subtract(days, 'days').toDate() let txEnd = moment().utc().startOf('date').toDate() - //await processTokenRewards(start, txEnd, days) + if (!steemOnlyReward){ + console.log('processTokenRewards'); + await processTokenRewards(start, txEnd, days) + } var d = new Date(); var dayId = d.getDay(); // Check if today is Monday, to calculate steem rewards if (dayId == 1){ + //console.log('processSteemRewards'); processSteemRewards(txEnd) } } @@ -84,7 +96,7 @@ async function processTokenRewards (start, end, days) { if (!end) end = moment().utc().startOf('date').toDate() let note = 'Delegation Reward For ' + moment(end).subtract(1, 'days').format('MMMM Do YYYY') - let acumulatedSteemPower = await getAcumulatedSteemPower(start, end) + let acumulatedSteemPower = await getAcumulatedSteemPower(start, end, true); //handles maintaining max CAP for payments let multiplier = 1 @@ -100,6 +112,18 @@ async function processTokenRewards (start, end, days) { console.log(">>>>multiplier:"+multiplier); //go through all delegators, and send out AFIT rewards for (let user of acumulatedSteemPower.users) { + //skip opt out users from reward + var user_opted_out = false; + for (var n = 0; n < config.exclude_rewards.length; n++) { + if (user.user == config.exclude_rewards[n]){ + console.log('User '+user.user+' opted out from rewards'); + user_opted_out = true; + break; + } + } + if (user_opted_out){ + continue; + } let reward = { user: user.user, token_count: parseFloat((user.totalSteem * multiplier).toFixed(3)), @@ -108,9 +132,12 @@ async function processTokenRewards (start, end, days) { date: end } console.log(reward) + //only send out funds if not a test run + if (!testRun){ upsertRewardTransaction(reward) } } +} async function processSteemRewards (start) { if (!start) start = moment().utc().startOf('date').toDate() @@ -118,16 +145,32 @@ async function processSteemRewards (start) { console.log(config.pay_account) const to = moment(start).subtract(7, 'days').toDate() const from = moment(to).subtract(7, 'days').toDate() - Promise.all([getAcumulatedSteemPower(from, to), getBenefactorRewards(to, start, -1)]).then(values => { + Promise.all([getAcumulatedSteemPower(from, to, true), getBenefactorRewards(to, start, -1)]).then(values => { const activeDelegations = values[0].users const steemRewards = values[1] const totalDelegatedSteem = values[0].totalSteem const rewardPerSteem = steemRewards / totalDelegatedSteem const rewards = _.map(activeDelegations, function (o) { - let reward = { + //skip opt out users from reward + var user_opted_out = false; + for (var n = 0; n < config.exclude_rewards.length; n++) { + if (o.user == config.exclude_rewards[n]){ + console.log('User '+o.user+' opted out from Steem rewards'); + user_opted_out = true; + break; + } + } + + let reward = {}; + if (!user_opted_out){ + reward = { user: o.user, steem: +(o.totalSteem * rewardPerSteem).toFixed(3) } + } + + + /*let url = 'https://v2.steemconnect.com/sign/transfer?from=[PAY_ACCOUNT]&to=[TO_ACCOUNT]&amount=[AMOUNT]%20STEEM&memo=Delegation%20Rewards' url = url.replace('[PAY_ACCOUNT]', config.pay_account) url = url.replace('[TO_ACCOUNT]', reward.user) @@ -144,7 +187,11 @@ async function processSteemRewards (start) { } var fs = require('fs'); - fs.writeFile("steemrewards.json", JSON.stringify(rewards), function(err) { + + var fileName = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + fileName = "steemrewards"+fileName+".json"; + console.log("fileName:"+fileName); + fs.writeFile(fileName, JSON.stringify(rewards), function(err) { if(err) { return console.log(err); } @@ -248,8 +295,9 @@ async function getBenefactorRewards (start, end, txStart, totalSp) { return +totalSp.toFixed(3) } -async function getActiveDelegations (start) { +async function getActiveDelegations (start, excludeOn) { start = new Date(start) + if (excludeOn){ return collection.aggregate( [ { $match: { 'tx_date': { '$lte': start } } }, @@ -275,6 +323,33 @@ async function getActiveDelegations (start) { { $sort: { tx_date: 1 } } ] ).toArray() + }else{ + return collection.aggregate( + [ + { $match: { 'tx_date': { '$lte': start } } }, + { $sort: { delegator: 1, tx_date: 1 } }, + { + $group: + { + _id: '$delegator', + steem_power: { $last: '$steem_power' }, + vests: { $last: '$vesting_shares' }, + tx_date: { $last: '$tx_date' } + } + }, + { $project: + { + _id: '$_id', + delegator: '$_id', + steem_power: 1, + tx_date: start + } + }, + { $match: { 'steem_power': { '$gt': 0 } } }, + { $sort: { tx_date: 1 } } + ] + ).toArray() + } } /* @@ -311,7 +386,7 @@ async function getCurrentTotalSP(toDate){ //}); } -async function getAcumulatedSteemPower (from, to) { +async function getAcumulatedSteemPower (from, to, excludeOn) { let result = { users: [] } @@ -319,12 +394,21 @@ async function getAcumulatedSteemPower (from, to) { from = moment(from).toDate() to = moment(to).toDate() // Get active delegations for the week - let activeDelegations = await getActiveDelegations(from) + let activeDelegations = await getActiveDelegations(from, excludeOn) // Get transactions of the processed week - let weekTxs = await db.collection('delegation_transactions').find( + let weekTxs + if (excludeOn){ + console.log('excluding users'); + weekTxs = await db.collection('delegation_transactions').find( {'tx_date': {$gt: from, $lt: to}, 'delegator': {$nin: config.exclude_rewards}}) .sort({tx_date: 1}).toArray() + }else{ + console.log('no exclude'); + weekTxs = await db.collection('delegation_transactions').find( + {'tx_date': {$gt: from, $lt: to}}) + .sort({tx_date: 1}).toArray() + } let allTxs = activeDelegations.concat(weekTxs) let groupedTxs = _.groupBy(allTxs, 'delegator') for (let index in groupedTxs) { From 5c5dbcba6c06a5c926c5a1e7440c215abe15b644 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 30 Sep 2018 23:53:39 +0300 Subject: [PATCH 036/193] Save data fixes (banned users, self votes..) implementing dynamic banned approach on save data skip self votes from reward fix for improper activity count valuation adding limit on max acceptable activity count --- save-data.js | 84 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 21 deletions(-) diff --git a/save-data.js b/save-data.js index f6ddff7..dbb3bf4 100644 --- a/save-data.js +++ b/save-data.js @@ -29,16 +29,16 @@ MongoClient.connect(url, function(err, client) { // Get the documents collection collection = db.collection(collection_name); - // client.close(); - //processVotedPosts(); - //getReblogs(); updateUserTokens(); - getPosts(); - setInterval(getPosts, 300 * 1000); - setInterval(updateUserTokens, 450 * 1000); + runPostsProcess(); + //run every 31 mins + setInterval(runPostsProcess, 31 * 60 * 1000); + //run every 40 mins + setInterval(updateUserTokens, 41 * 60 * 1000); + } else { utils.log(err, 'import'); - //mail.sendPlainMail('Database Error', err, '') + /*mail.sendPlainMail('Database Error', err, '') .then(function(res, err) { if (!err) { console.log(res); @@ -46,15 +46,24 @@ MongoClient.connect(url, function(err, client) { utils.log(err, 'import'); } }); - process.exit(); + process.exit();*/ } }); -function getPosts(index) { - if(postsProcessing) + +function runPostsProcess(){ + if(postsProcessing){ return; + } postsProcessing = true; + getPosts(); +} + +async function getPosts(index) { + + console.log('>>>>>>>> attempt getPosts <<<<<<<<<<<'); + console.log('---- Getting Posts ----'); var query = {tag: config.main_tag, limit: 100}; if (index) { @@ -62,22 +71,41 @@ function getPosts(index) { query.start_author = index.start_author; query.start_permlink = index.start_permlink; } + + //grab banned user list before rewarding + var banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); + console.log('found banned users'); + steem.api.getDiscussionsByCreated(query, function (err, result) { if (result && !err) { if(result.length == 0 || !result[0]) { utils.log('No posts found for this tag: ' + config.main_tag, 'import'); + postsProcessing = false; return; } console.log('Post count: ' + result.length); - let posts = utils.filterPosts(result, config.account, config.main_tag); + + + let posts = utils.filterPosts(result, banned_users, config.account, config.main_tag); + + //if the result was not an array, bail out + if (posts == -1){ + console.log('done looking for posts'); + + postsProcessing = false; + return; + } + console.log('Filtered count: ' + posts.length); // Upsert posts var bulk = collection.initializeUnorderedBulkOp(); + console.log('check post'); + var step_count = -1; for(var i = 0; i < posts.length; i++) { let post = posts[i] try { post.json_metadata = JSON.parse(post.json_metadata); - let step_count = post.json_metadata.step_count; + step_count = post.json_metadata.step_count; if (step_count < 5000) continue; else if (step_count < 6000) @@ -90,6 +118,8 @@ function getPosts(index) { post.token_rewards = 65; else if(step_count < 10000) post.token_rewards = 80; + else if(step_count > 150000) + continue; else post.token_rewards = 100; } catch (err) { @@ -101,7 +131,13 @@ function getPosts(index) { post ); } - + //do not attempt insertion if no results found on this round + if (posts.length == 0){ + let last_post = result[result.length - 1]; + if (!index || (index.start_permlink != last_post.permlink && index.start_author != last_post.author && result.length >= 100)){ + return getPosts({start_author: last_post.author, start_permlink: last_post.permlink}); + } + }else{ bulk.execute() .then(async function (res) { var mes = res.nInserted + ' posts inserted - ' + res.nUpserted + ' posts upserted - ' + res.nModified + ' posts updated'; @@ -109,8 +145,6 @@ function getPosts(index) { let last_post = posts[posts.length - 1]; await processTransactions(posts); console.log('Inserted transactions'); - //appending fix for potential caught loop - postsProcessing = false; if (!index || (index.start_permlink != last_post.permlink && index.start_author != last_post.author && result.length >= 100)) return getPosts({start_author: last_post.author, start_permlink: last_post.permlink}); console.log('No more new posts'); @@ -118,7 +152,7 @@ function getPosts(index) { }) .catch(function (err) { utils.log(err, 'import'); - mail.sendPlainMail('Error en mongo upsert', err, config.report_emails) + /*mail.sendPlainMail('Error en mongo upsert', err, config.report_emails) .then(function(res, err) { if (!err) { console.log(res); @@ -132,9 +166,11 @@ function getPosts(index) { //making sure we don't get caught up in infinite loop after some error postsProcessing = false; }); + + } } else { utils.log(err, 'import'); - mail.sendPlainMail('0 posts...', err, config.report_emails) + /*mail.sendPlainMail('0 posts...', err, config.report_emails) .then(function(res, err) { if (!err) { console.log(res); @@ -143,7 +179,7 @@ function getPosts(index) { console.log(err); return; } - }); + });*/ } }); } @@ -181,6 +217,8 @@ async function processTransactions(posts) { .upsert().replaceOne(post_transaction); transactions.push(post_transaction); post.active_votes.forEach(async vote => { + //skip self vote from rewards + if (post.author != vote.voter){ let vote_transaction = { user: vote.voter, reward_activity: 'Post Vote', @@ -196,10 +234,11 @@ async function processTransactions(posts) { }) .upsert().replaceOne(vote_transaction); transactions.push(vote_transaction); + } }); let reblogs = await steem.api.getRebloggedByAsync(post.author, post.permlink); console.log('------------------ REBLOGS --------------------'); - // console.log(reblogs); + console.log(reblogs); reblogs.forEach(async reblog => { if(reblog != post.author){ let reblog_transaction = { @@ -228,6 +267,8 @@ async function processTransactions(posts) { async function updateUserTokens() { console.log('---- Updating Users ----'); + + try{ let query = await db.collection('token_transactions').aggregate([ { $group: { _id: "$user", tokens: { $sum: "$token_count" } } }, { $sort: { tokens: -1 } }, @@ -238,7 +279,7 @@ async function updateUserTokens() { } } ]) - try{ + let user_tokens = await query.toArray(); await db.collection('user_tokens').remove({}); return await db.collection('user_tokens').insert(user_tokens); @@ -296,9 +337,10 @@ async function upsertPosts(posts) { var bulk = collection.initializeUnorderedBulkOp(); for(var i = 0; i < posts.length; i++) { let post = posts[i] + var step_count = -1; try { post.json_metadata = JSON.parse(post.json_metadata); - let step_count = post.json_metadata.step_count; + step_count = post.json_metadata.step_count; if (step_count < 5000) continue; else if (step_count < 6000) From d9ca43a52cf28c0ee75b80c569377e067d5245af Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Oct 2018 18:01:01 +0300 Subject: [PATCH 037/193] Fix issue with non-existing users Rank Implement a fix for cases of non-existing users causing error on data return --- app.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 9ca75c3..23c1ed6 100644 --- a/app.js +++ b/app.js @@ -420,7 +420,10 @@ app.get('/getRank/:user', async function (req, res) { var userDelegations = await activeDelegationFunc(req, res); //console.log(userDelegations.steem_power); - var delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(userDelegations.steem_power)); + var delegation_score = 0; + if (userDelegations != null){ + delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(userDelegations.steem_power)); + } user_rank += delegation_score; @@ -428,7 +431,10 @@ app.get('/getRank/:user', async function (req, res) { var userTokens = await grabUserTokensFunc(req,res); //console.log(userTokens.tokens); - var afit_tokens_score = utils.calcScore(afit_token_rules, config.afit_token_factor, parseFloat(userTokens.tokens)); + var afit_tokens_score = 0; + if (userTokens != null){ + afit_tokens_score = utils.calcScore(afit_token_rules, config.afit_token_factor, parseFloat(userTokens.tokens)); + } user_rank += afit_tokens_score; From b1ad7e8f5747dd8c4c825f1afab13a8dd962c7fc Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Oct 2018 18:02:51 +0300 Subject: [PATCH 038/193] Adjust Comment Template Modify the comment template to be better aligned with new reward system --- comment.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/comment.md b/comment.md index f5625b4..8287b3d 100644 --- a/comment.md +++ b/comment.md @@ -1,3 +1,13 @@ -Congrats! You just reached **{milestone}** via using Actifit fitness tracker and provided **Proof of Activity**! -You accordingly gained {token_count} Actifit tokens for attaining {step_count} steps! -You also received an {weight}% upvote via @actifit account. \ No newline at end of file +Congrats on providing **Proof of Activity** via your Actifit report! +You have accordingly been rewarded {token_count} AFIT tokens for your effort in reaching {step_count} activity, as well as your user rank and report quality! +You also received an {weight}% upvote via @actifit account. +Actifit reward structure has changed recently, and the new rewards and upvotes are based on your: +- User rank: which depends on your delegated SP, accumulated AFIT tokens, rewarded post count and recent rewarded activity. +- Post score: which depends on your activity count, post content, post upvotes, quality comments, moderator review and user rank. +To improve your user rank, delegate more, pile up more AFIT tokens, and post more. +To improve your post score, get to the max activity count, work on improving your post content, improve your user rank, engage with the community to get more upvotes and quality comments. + +![rulersig2.jpg](https://cdn.steemitimages.com/DQmXrZz658YfMQBXNTA12rmbzqWXASfaGcNSqatJJ2ba7NR/rulersig2.jpg) +Chat with us on [discord](https://discord.gg/aHtcA6r) | Visit our [website](https://actifit.io/) +[Download on playstore](https://bit.ly/actifit-app) | [Download on app store](https://bit.ly/actifit-ios) +[FAQs](https://steemit.com/actifit/@katerinaramm/actifit-app-or-rewarding-fitness-activity-with-tokens-and-steemit-upvotes-faqs) | [Text Tutorial](https://steemit.com/utopian-io/@katerinaramm/tutorial-for-actifit-app-android) | [Video Tutorial](https://youtu.be/tqkaDoonyvI) \ No newline at end of file From 29f03f2aaeb6629e800f135919b95cc08d3d4e2d Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Oct 2018 18:09:59 +0300 Subject: [PATCH 039/193] Implement New Actifit V2 Reward System - Implement full reward system from bottom up to include activity, content, upvote, engagement, moderation, user rank aspects for better user rewards - Combine upvoting with AFIT token rewards (previously 2 separate functionalities) leading now to instantaneous token rewards and total amount calculation post reward session. - Implement additional JSON data to the comments to better reflect and store score and reward-relevant information - Implement additional testing details for thorough local testing - Implement claim rewards functionality before upvote to maximize rewards --- curation-bot.js | 549 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 472 insertions(+), 77 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 0ba941b..264aaf5 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -6,6 +6,9 @@ var _ = require('lodash'); var moment = require('moment'); const MongoClient = require('mongodb').MongoClient; +const cheerio = require('cheerio') +const axios = require('axios'); + var account = null; var last_trans = 0; var members = []; @@ -18,10 +21,90 @@ var vote_time; var last_votes = Array(); var skip = false; var version = '0.3.4'; + +//version of the reward system +var reward_sys_version = 'v0.2'; + var error_sent = false; var crypto = require('crypto'); +const activity_rules = [ + [4999,0], + [5999,0.20], + [6999,0.35], + [7999,0.50], + [8999,0.65], + [9999,0.80], + [10000,1.00] +] + +const content_rules = [ + [99,0], + [399,0.20], + [799,0.35], + [1199,0.50], + [1599,0.65], + [1999,0.80], + [2000,1.00] +] + +const img_rules = [ + [0,0], + [1,0.2], + [2,0.4], + [3,0.6], + [4,0.8], + [5,1] +] + +const vid_rules = [ + [0,0], + [1,1] +] + +const upv_rules = [ + [0,0], + [10,0.2], + [20,0.4], + [30,0.6], + [50,0.8], + [100,1] +] + +const cmts_rules = [ + [0,0], + [2,0.2], + [4,0.4], + [6,0.6], + [8,0.8], + [10,1] +] + +//our pre-defined image pool +const actifit_img_urls = [ + "https://cdn.steemitimages.com/DQmXv9QWiAYiLCSr3sKxVzUJVrgin3ZZWM2CExEo3fd5GUS/sep3.png", + "https://cdn.steemitimages.com/DQmRgAoqi4vUVymaro8hXdRraNX6LHkXhMRBZxEo5vVWXDN/ACTIVITYCOUNT.png", + "https://cdn.steemitimages.com/DQmZ6ZT8VaEpaDzB16qZzK8omffbWUpEpe4BkJkMXmN3xrF/ACTIVITYTYPE.png", + "https://cdn.steemitimages.com/DQmdnh1nApZieHZ3s1fEhCALDjnzytFwo78zbAY5CLUMpoG/TRACKM.png", + "https://cdn.steemitimages.com/DQmfSsFiXem7AxWG1NCiYYPAjtT4Y7LR8FsXpfsZQe7XqPC/h1.png", + "https://cdn.steemitimages.com/DQmVqJVEWUwicFRtkEz2WYq2mDH61mQLDsrzN1yBrKLrpyZ/w1a.png", + "https://cdn.steemitimages.com/DQmPJ2Vvi3mBQXKHoy5CTG7fyLFWMG8JaAZ8y1XZFeDkRUC/bd1.png", + "https://cdn.steemitimages.com/DQmZ2Lfwg77FLaf3YpU1VPLsJvnBt1F8DG8y6t6xUAKnsYq/w1.png", + "https://cdn.steemitimages.com/DQmbbAAFy6hwwBWqtSmcSwosTyNZi9rcd6GNeugQRY9MF1h/t1.png", + "https://cdn.steemitimages.com/DQmbaoNBT5Unnjqh8JgP6TPj4mFKFnyKkLgP6eDYnnkiLkB/c1.png", + + "https://cdn.steemitimages.com/DQmQqfpSmcQtfrHAtzfBtVccXwUL9vKNgZJ2j93m8WNjizw/l5.png", + "https://cdn.steemitimages.com/DQmbWy8KzKT1UvCvznUTaFPw6wBUcyLtBT5XL9wdbB7Hfmn/l6.png", + + "https://cdn.steemitimages.com/DQmNp6YwAm2qwquALZw8PdcovDorwaBSFuxQ38TrYziGT6b/A-20.png", + "https://cdn.steemitimages.com/DQmY5UUP99u5ob3D8MA9JJW23zXLjHXHSRofSH3jLGEG1Yr/A-10.png", + "https://cdn.steemitimages.com/DQmRDW8jdYmE37tXvM6xPxuNnzNQnUJWSDnxVYyRJEHyc9H/A-14.png", + "https://cdn.steemitimages.com/DQmPscjCVBggXvJT2GaUp66vbtyxzdzyHuhnzc38WDp4Smg/A-3.png", "https://cdn.steemitimages.com/DQmVoLkmU47N4fM75HVY7se7JiMzdXhQKQUZ5fyCDwh1BrE/A-13.png", + "https://cdn.steemitimages.com/DQmcngR7AdBJio52C5stkD5C7vgsQ1yDH57Lb4J96Pys4a9/A-6.png","https://cdn.steemitimages.com/DQmdL69SXfqqKKoaEC55u3wsiMyAhcSErdK1fYjckAUyMCz/A-2.png", "https://cdn.steemitimages.com/DQmRgZTP4R6q9DfAWf9dNuqXWgvxkduxuH5QJfeyUVEqsk9/A-8.png", "https://cdn.steemitimages.com/DQmWzwdS5u4G1GheceM1bmBC3HL6zWubUGYbPkCmEcEDXrD/A-4.png", "https://cdn.steemitimages.com/DQmUVjgmJHvtbYB2APdxqNxxkZeJ2KvPeXEE7v3BpxGJkbR/A-18.png", "https://cdn.steemitimages.com/DQmdMW7LzuiKLi9vaEWsXnWGcU1oMHbF4983L16CE63dvwz/A-17.png", + "https://cdn.steemitimages.com/DQmVD3pXR4EHzYeCapMNSanTeK9wGJeJ24XYJhZSUmjJReR/A-11.png", "https://cdn.steemitimages.com/DQmY67NW9SgDEsLo2nsAw4nYcddrTjp4aHNLyogKvGuVMMH/A-9.png", "https://cdn.steemitimages.com/DQmcrdacUAEHoeiX9gNVAiiL5iydmJoPve2nXpzszNtJZPb/A-12.png", "https://cdn.steemitimages.com/DQmbP8GuFvcHUyh7bKDheDN5iz8ERPCYzMaSVoRT2R5ZYPE/A-15.png","https://cdn.steemitimages.com/DQmW1VsUNbEjTUKawau4KJQ6agf41p69teEvdGAj1TMXmuc/A-5.png", + "https://cdn.steemitimages.com/DQmeBn1PLf6a3QaXjM23EbQcaKtfDckgtGPHE4DApoUeBEJ/A-1.png", "https://cdn.steemitimages.com/DQmV7NRosGCmNLsyHGzmh4Vr1pQJuBPEy2rk3WvnEUDxDFA/A-21.png", "https://cdn.steemitimages.com/DQmdNAWWwv6MAJjiNUWRahmAqbFBPxrX8WLQvoKyVHHqih1/A-19.png","https://cdn.steemitimages.com/DQmVNqM8wQj2TnfwqSPYtfAuPHYjeBXSFekCHGZw9K3B9Gi/A-16.png", "https://cdn.steemitimages.com/DQma7nn1yV2w9iY6qXDBJUoTWkELTYxot7R9eoG1M3Tbtqn/A-7.png"]; + steem.api.setOptions({ url: 'https://api.steemit.com' });//https://gtg.steem.house:8090 //steem.api.setOptions({ url: 'https://gtg.steem.house:8090' }); @@ -33,15 +116,22 @@ var botNames; // Connection URL -const url = config.mongo_uri; +var url = config.mongo_uri; + +//check if this is a test scenario to use local DB url +if (config.testing){ + url = config.mongo_local; +} +console.log('db url:'+url); var db; var collection; -// Database Name -const db_name = config.db_name; + +var db_name = config.db_name; const collection_name = 'banned_accounts'; var banned_users; +var moderator_list; // Check if bot state has been saved to disk, in which case load it if (fs.existsSync('state.json')) { @@ -86,7 +176,11 @@ startProcess(); // Schedule to run every minute +if (!config.testing){ setInterval(startProcess, 60 * 1000); +}else{ + setTimeout(startProcess, 20 * 1000); +} var votePosts; @@ -107,8 +201,6 @@ async function startProcess() { else { account = result[0]; - // Check if there are any rewards to claim. - claimRewards(); } }); @@ -117,7 +209,7 @@ async function startProcess() { //deactivating condition of 24 hrs to pass var passedOneDay = true;//today >= oneMoreDay; - console.log('found banned users'); + //console.log('found banned users'); //console.log(banned_users); /*for (var n = 0; n < banned_users.length; n++) { @@ -138,18 +230,49 @@ async function startProcess() { utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); console.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); + // We are at voting power kick start - time to vote! //console.log(vp >= parseFloat(config.vp_kickstart)/100); - if (vp >= parseFloat(config.vp_kickstart)/100) { + if (vp >= parseFloat(config.vp_kickstart)/100 || config.testing) { + // Check if there are any rewards to claim before voting + if (!config.testing){ + claimRewards(); + } + console.log('lets vote'); skip = true; + console.log('fetch banned users list'); //grab banned user list before rewarding banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); + //grab list of moderators + var moderator_api_url = config.api_url+'moderators'; + var moderator_info = await axios.get(moderator_api_url); + console.log(moderator_info.data); + var moderator_array = moderator_info.data; + moderator_list = []; + for (var mod_it=0;mod_itconfig.account_claim_rc_min){ + //if we reached min threshold, claim more spots for discounted accounts + utils.claimDiscountedAccount(); + } + + }, function(err) { + console.log("Error fetching RC"); + console.log(err); + }); } } else if(skip) @@ -158,16 +281,23 @@ async function startProcess() { console.log('Loading account data...'); else console.log('Voting... or waiting for a day to pass'); } - +//var post_scores = []; function processVotes(query, subsequent) { - steem.api.getDiscussionsByCreated(query, function (err, result) { + steem.api.getDiscussionsByCreated(query, async function (err, result) { if (result && !err) { is_voting = true; utils.log(result.length + ' posts to process...'); + //initialize inserting posts to db + + var bulk = db.collection('posts').initializeUnorderedBulkOp(); + + //connect to the token_transactions table to start rewarding + var bulk_transactions = db.collection('token_transactions').initializeUnorderedBulkOp(); + for(var i = 0; i < result.length; i++) { var post = result[i]; @@ -185,11 +315,6 @@ function processVotes(query, subsequent) { query['start_permlink'] = post.permlink; query['start_author'] = post.author; } - // Make sure the post is less than 6.5 days - /*if((new Date() - new Date(post.created + 'Z')) >= (6.5 * 24 * 60 * 60 * 1000)) { - utils.log('This post is too old for a vote: ' + post.url); - continue; - }*/ // Make sure the post is older than config time if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { @@ -272,36 +397,15 @@ function processVotes(query, subsequent) { if((new Date() - new Date(post.created + 'Z')) >= (config.max_days * 24 * 60 * 60 * 1000)) { continue; } - var step_count = -1; + try { + post.json = JSON.parse(post.json_metadata); - step_count = post.json.step_count; - if (step_count < 5000) - continue; - else if (step_count < 6000) - post.rate_multiplier = 0.2; - else if(step_count < 7000) - post.rate_multiplier = 0.35; - else if(step_count < 8000) - post.rate_multiplier = 0.5; - else if(step_count < 9000) - post.rate_multiplier = 0.65; - else if(step_count < 10000) - post.rate_multiplier = 0.8; - else if(step_count > 150000) - continue; - else - post.rate_multiplier = 1; - } catch (err) { - utils.log('Error parsing json metadata'); - console.log(err); - continue; - } //check if the post has an encryption key val, and ensure it is the proper one if (post.json.actiCrVal){ - var txt_to_encr = post.author + post.permlink + step_count ; + var txt_to_encr = post.author + post.permlink + post.json.step_count ; var cipher = crypto.createCipher(config.encr_mode, config.encr_key); let encr_txt = cipher.update(txt_to_encr, 'utf8', 'hex'); encr_txt += cipher.final('hex'); @@ -317,11 +421,155 @@ function processVotes(query, subsequent) { continue; } + /**************** Post Score calculation section *******************/ + + /******************* activity count criteria *********************/ + + //calculate activity count score + post.activity_score = utils.calcScore(activity_rules, config.activity_factor, post.json.step_count); + + //skip post if it has less than min activity recorded + if (post.activity_score == 0){ + continue; + } + + /******************* content criteria *********************/ + const $ = cheerio.load('
'+post.body+'
'); + + //grab text without HTML, and remove extra spacing + var pure_text = $('.actifit_container').text().replace(/\s+/g,' '); + + //calculate content score + post.content_score = utils.calcScore(content_rules, config.content_factor, pure_text.length); + + /******************* media criteria *********************/ + + + //grab proper images, skipping our default images + + var new_imgs = 0; + + var recorded_imgs = []; + //go through each image from the content and check if it matches one of our existing images + $('img').each(function(i, elem) { + //if this image is not part of ours, add it + if (!actifit_img_urls.includes($(this).attr('src'))){ + new_imgs += 1; + recorded_imgs.push($(this).attr('src')); + //console.log($(this).attr('src')); + } + }); + + //grab listing of recorded images as part of json + var json_img_list = post.json.image; + + //console.log(json_img_list); + + //try to see if some images were not captured by our approach for HTML content, and grab them from json meta + if (json_img_list.length>0){ + for (let img_entry of json_img_list) { + //if this image is not part of ours, add it + if (!actifit_img_urls.includes(img_entry) && !recorded_imgs.includes(img_entry)){ + new_imgs += 1; + recorded_imgs.push(img_entry); + } + }; + } + + //console.log('2>>>new_imgs:'+new_imgs); + + //console.log('>>>> unique images:'+new_imgs); + //calculate img score + post.media_score = utils.calcScore(img_rules, config.media_factor, new_imgs); + + /******************* upvote criteria *********************/ + + //calculate upvote score relying on positive votes only + post.upvote_score = utils.calcScore(upv_rules, config.upvotes_factor, post.net_votes); + //console.log('upvotes:'+post.net_votes); + + /***************** moderator upvote factor ******************/ + + //check if a moderator upvoted the post to give it better reward + post.moderator_score = 0; + post.active_votes.some(function(vote){ + if (moderator_list.includes(vote.voter)){ + post.moderator_score = parseInt(config.moderator_upvote_factor); + //console.log('found moderator upvote'+vote.voter); + return true; + } + }); + + //console.log(post.moderator_score); + + /******************* comments criteria *********************/ + + + let comments = await steem.api.getContentRepliesAsync(post.author, post.permlink); + var matching_comment_count = 0; + for(var cmt_it = 0; cmt_it < comments.length; cmt_it++) { + //console.log('>>>>>>'+comments[cmt_it].body); + const $ = cheerio.load('
'+comments[cmt_it].body+'
'); + var comment_pure = $('.comment_container').text().replace(/\s+/g,' '); + //console.log(comment_pure); + if (comment_pure.length > 50){ + matching_comment_count += 1; + } + + //check if the comment is made by a moderator, if it is we need to reward the moderator + if (moderator_list.includes(comments[cmt_it].author)){ + let comment_transaction = { + user: comments[cmt_it].author, + reward_activity: 'Moderator Comment', + token_count: parseInt(config.moderator_comment_reward), + url: post.url, + comment_url: comments[cmt_it].url, + date: comments[cmt_it].created + } + bulk_transactions.find( + { + user: comment_transaction.user, + reward_activity: comment_transaction.reward_activity, + url: comment_transaction.url, + comment_url: comment_transaction.comment_url + }).upsert().replaceOne(comment_transaction); + console.log('found comment>>>>'); + console.log(comment_transaction); + } + } + //console.log("comments:"+matching_comment_count); + //calculate comment score + post.comment_score = utils.calcScore(cmts_rules, config.comments_factor, matching_comment_count); + + /******************* user rank criteria *********************/ + //var request = require('request'); + var rank_api_url = config.api_url+'getRank/'+post.author; + var user_rank_info = await axios.get(rank_api_url); + //console.log(user_rank_info.user_rank); + post.user_rank = user_rank_info.data.user_rank; + //calculate user rank score relying on positive votes only + post.user_rank_score = parseFloat(user_rank_info.data.user_rank)*parseInt(config.rank_factor)/100; + //console.log('rank'+post.user_rank_score); + + + //calculate total post score + post.post_score = post.activity_score + post.content_score + post.media_score + post.upvote_score + post.comment_score + post.moderator_score + post.user_rank_score; + + //rate multiplier to allow assigning proper steem upvote value per each post according to its post_score/afit payout + post.rate_multiplier = post.post_score / 100; + //post_scores.push([post.url,post.post_score]); + //console.log(post); + + } catch (err) { + console.log('Error parsing json metadata'); + console.log(err); + continue; + } let last_index = _.findLastIndex(votePosts, ['author', post.author]); if (last_index != -1) { - console.log('---- User already has vote ------'); + console.log('---- User already has post same date ------'); let last_voted = votePosts[last_index]; var last_date = moment(last_voted.created).format('D'); var this_date = moment(post.created).format('D'); @@ -336,15 +584,104 @@ function processVotes(query, subsequent) { console.log('---- Moment-----'); console.log(last_date); console.log(this_date); + continue; } - } else { - console.log('Voting on: ' + post.url); + //console.log('Voting on: ' + post.url); votePosts.push(post); } + try{ + console.log('going through '+post.url); + //insert post if not inserted before + bulk.find( { permlink: post.permlink } ).upsert().replaceOne( + post + ); + + //post token rewards DB transaction + + //by default the reward owner is the author + var reward_user = post.author; + var activity_type = 'Post'; + var note = ''; + var result; + //if we find this is a charity run, let's switch it to the actual charity name + if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ + reward_user = post.json.charity; + activity_type = 'Charity Post'; + note = 'Charity donation via activity by user '+post.author; + } + let post_transaction = { + user: reward_user, + reward_activity: activity_type, + token_count: post.post_score, + url: post.url, + date: post.created, + note: note, + reward_system: reward_sys_version + } + + bulk_transactions.find( + { + user: post_transaction.user, + reward_activity: post_transaction.reward_activity, + url: post_transaction.url + }).upsert().replaceOne(post_transaction); + + //reward upvoters + //make sure we already have a positive rshares + var total_post_upv_shares = parseInt(post.vote_rshares); + if (total_post_upv_shares>0){ + + //calculate max token payment based upon post pending payout + var max_afits = Math.min(parseFloat(post.pending_payout_value) * parseFloat(config.per_post_alloc_afits), parseFloat(config.per_post_alloc_afits)); + //console.log('max afits '+max_afits); + post.active_votes.forEach(async vote => { + + //grab user's contribution to the upvote pool + var upv_tokens = parseInt(vote.rshares); + + //skip self vote from rewards and make sure this is a positive upvote + if (post.author != vote.voter && upv_tokens>0){ + //calculate the percentage of the user's contribution, and allocate him his AFIT tokens share + var voter_tokens = upv_tokens / total_post_upv_shares * max_afits; + voter_tokens = parseFloat(voter_tokens.toFixed(3)); + let vote_transaction = { + user: vote.voter, + reward_activity: 'Post Vote', + token_count: voter_tokens, + url: post.url, + date: vote.time } - /*let testPost = {rate_multiplier: 0.8}; - votePosts.push(testPost);*/ + bulk_transactions.find( + { + user: vote_transaction.user, + reward_activity: vote_transaction.reward_activity, + url: vote_transaction.url + }).upsert().replaceOne(vote_transaction); + //transactions.push(vote_transaction); + + //console.log(vote_transaction); + } + }); + } + //result = posts_collection.insert(post); + }catch(err){ + console.log(err); + } + }//end of loop going through posts + + if (votePosts.length>0){ + try{ + //store posts + await bulk.execute(); + //award transaction tokens + bulk_transactions.execute(); + }catch(bulkerr){ + console.log(bulkerr); + } + } + + //if this is the first try, or the new count of posts is bigger than the one before, let's try adding again if (!subsequent || votePosts.length>lastIterationCount){ @@ -360,22 +697,28 @@ function processVotes(query, subsequent) { if (votePosts.length > 0) { utils.log(votePosts.length + ' posts to vote...'); - vote_data = utils.calculateVotes(votePosts, config.vote_weight); + var vote_data = utils.calculateVotes(votePosts, config.vote_weight); votePosts.sort(function(post1, post2) { - // Ascending: first age less than the previous - return post1.json.step_count - post2.json.step_count; + //Sort posts by reverse score, so as when popping them we get sorted by highest + return post1.post_score - post2.post_score; }); - //utils.log(vote_data.total_votes + ' total votes to divide.'); + utils.log(vote_data.power_per_vote + ' power per full vote.'); - utils.log(vote_data.power_per_vote * 0.8 + ' power per second vote.'); + /*utils.log(vote_data.power_per_vote * 0.8 + ' power per second vote.'); utils.log(vote_data.power_per_vote * 0.65 + ' power per third vote.'); utils.log(vote_data.power_per_vote * 0.5 + ' power per fourth vote.'); utils.log(vote_data.power_per_vote * 0.35 + ' power per fifth vote.'); - utils.log(vote_data.power_per_vote * 0.2 + ' power per lowest vote.'); - if(config.testing) - return; - else + utils.log(vote_data.power_per_vote * 0.2 + ' power per lowest vote.');*/ + + var tot_weight = 0; + for (var xx=0;xx { + if(config.testing){ + //resolve(''); + if(config.comment_location && config.comment){ + setTimeout(function () { + sendComment(post, vote_weight) + .then( res => { + resolve('') + }) + .catch(err => { + reject(err); + }) + }, 3000); + }else{ + resolve(''); + } + }else{ steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { if (!err && result) { utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); - if(config.comment_location && config.comment) + if(config.comment_location && config.comment){ setTimeout(function () { - sendComment(post.author, post.permlink, vote_weight, post.rate_multiplier, post.json.step_count) + sendComment(post, vote_weight) .then( res => { resolve(res) }) @@ -448,8 +810,9 @@ function sendVote(post, retries, power_per_vote) { reject(err); }) }, 3000); - else + }else{ resolve(result); + } } else { utils.log(err, result); @@ -464,10 +827,45 @@ function sendVote(post, retries, power_per_vote) { } } }); + } }); } -function sendComment(parentAuthor, parentPermlink, vote_weight, rate_multiplier, post_step_count) { + +//function handles updating current user token count +async function updateUserTokens() { + console.log('---- Updating Users ----'); + + try{ + //group all token transactions per user, and sum them to generate new total count + let query = await db.collection('token_transactions').aggregate([ + { $group: { _id: "$user", tokens: { $sum: "$token_count" } } }, + { $sort: { tokens: -1 } }, + { $project: { + _id: "$_id", + user: "$_id", + tokens: "$tokens", + } + } + ]) + + let user_tokens = await query.toArray(); + //remove old token count per user + await db.collection('user_tokens').remove({}); + //insert new count per user + await db.collection('user_tokens').insert(user_tokens); + }catch(err){ + console.log('>>save data error:'+err.message); + } +} + + +function sendComment(post, vote_weight) { + var parentAuthor = post.author; + var parentPermlink = post.permlink; + var rate_multiplier = post.rate_multiplier; + var post_step_count = post.json.step_count; + var content = null; // Return promise return new Promise((resolve, reject) => { @@ -479,26 +877,16 @@ function sendComment(parentAuthor, parentPermlink, vote_weight, rate_multiplier, // Generate the comment permlink via steemit standard convention var permlink = 're-' + parentAuthor.replace(/\./g, '') + '-' + parentPermlink + '-' + new Date().toISOString().replace(/-|:|\./g, '').toLowerCase(); - var token_count = parseFloat(rate_multiplier)*100; - var milestone_txt = "level 1 milestone"; - if(token_count < 36) - milestone_txt = "level 2 milestone"; - else if(token_count < 51) - milestone_txt = "level 3 milestone"; - else if(token_count < 66) - milestone_txt = "level 4 milestone"; - else if(token_count < 81) - milestone_txt = "level 5 milestone"; - else - milestone_txt = "the top level milestone"; - + var token_count = post.post_score;//parseFloat(rate_multiplier)*100; // Replace variables in the promotion content - content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{milestone\}/g, milestone_txt).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); + content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); - //adding proper meta content for later relevant reward via afit_tokens data - var jsonMetadata = { tags: ['actifit'], app: 'actifit/v'+version, afit_tokens: token_count }; + //replace(/\{milestone\}/g, milestone_txt). + //adding proper meta content for later relevant reward via afit_tokens data + var jsonMetadata = { tags: ['actifit'], app: 'actifit/v'+version, user_rank: post.user_rank, content_score: post.content_score, media_score: post.media_score, upvote_score: post.upvote_score, comment_score: post.comment_score, user_rank_score: post.user_rank_score, afit_tokens: token_count, moderator_score: post.moderator_score, post_score: post.activity_score }; + if (!config.testing){ // Broadcast the comment steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { if (!err && result) { @@ -509,8 +897,15 @@ function sendComment(parentAuthor, parentPermlink, vote_weight, rate_multiplier, reject(err); } }); - } else + }else{ + console.log('comment'); + console.log(content); + console.log(jsonMetadata); + resolve(''); + } + }else{ reject('Failed to load content'); + } }); // Check if the bot should resteem this post /* if (config.resteem) @@ -634,9 +1029,9 @@ function claimRewards() { // Send liquid post rewards to the specified account steem.broadcast.transfer(config.active_key, config.account, config.post_rewards_withdrawal_account, account.reward_sbd_balance, 'Liquid Post Rewards Withdrawal', function (err, response) { - if (err) + if (err){ utils.log(err, response); - else { + }else{ utils.log('$$$ Auto withdrawal - liquid post rewards: ' + account.reward_sbd_balance + ' sent to @' + config.post_rewards_withdrawal_account); } }); From e989543d7fe4ebc1b4dd89f2db3cebe9ba5b2895 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Oct 2018 18:12:10 +0300 Subject: [PATCH 040/193] Fix issue with reading beneficiary data Some change took place apparently leading to old beneficiary data fetching, as implemented by old developer, to not be properly fetched. We made a small change to our code to fix this issue. --- delegations.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/delegations.js b/delegations.js index 7fba4ff..3b44d86 100644 --- a/delegations.js +++ b/delegations.js @@ -279,7 +279,8 @@ async function getBenefactorRewards (start, end, txStart, totalSp) { let op = txs[1].op // Look for delegation operations if (op[0] === 'comment_benefactor_reward') { - let newSp = vestsToSteemPower(op[1].reward) + //console.log(op[1]); + let newSp = vestsToSteemPower(op[1].vesting_payout) totalSp = totalSp + newSp } } else if (date < start) break From bbe2dac64b7f2033f6dd64f7c0f68f785a7663fe Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Oct 2018 18:15:30 +0300 Subject: [PATCH 041/193] Implement Automated RC and Account Claim - Include an automated approach to check current RC, along with existing prior timed queries - Implement a claim discounted accounts based on a threshold to auto-accumulate discounted account spots --- utils.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/utils.js b/utils.js index 371a343..0a6dc55 100644 --- a/utils.js +++ b/utils.js @@ -2,6 +2,10 @@ var fs = require("fs"); const steem = require('steem'); var _ = require('lodash'); const axios = require('axios'); +const dsteem = require('dsteem'); + +const client = new dsteem.Client('https://api.steemit.com'); + var config; steem.api.setOptions({ url: 'https://api.steemit.com' }); @@ -66,6 +70,60 @@ var HOURS = 60 * 60; return currentManaPerc; } + //implement a get current Resource Credits function for normal operations consumption + async function getRC(account_name){ + var data={"jsonrpc":"2.0","id":1,"method":"condenser_api.get_account_count","params":{}}; + //return new Promise(function(fulfill,reject){ + //var request = require("request"); + let location = "https://api.steemit.com"; + var response = await axios.post(location, {"jsonrpc":"2.0","id":1,"method":"rc_api.find_rc_accounts","params":{"accounts":[account_name]}}); + + console.log(response.data.result.rc_accounts); + + + + const STEEM_RC_MANA_REGENERATION_SECONDS =432000; + const estimated_max = parseFloat(response.data.result.rc_accounts["0"].max_rc); + const current_mana = parseFloat(response.data.result.rc_accounts["0"].rc_manabar.current_mana); + const last_update_time = parseFloat(response.data.result.rc_accounts["0"].rc_manabar.last_update_time); + const diff_in_seconds = Math.round(Date.now()/1000-last_update_time); + let estimated_mana = (current_mana + diff_in_seconds * estimated_max / STEEM_RC_MANA_REGENERATION_SECONDS); + if (estimated_mana > estimated_max) + estimated_mana = estimated_max; + const estimated_pct = estimated_mana / estimated_max * 100; + const res= {"current_mana": current_mana, "last_update_time": last_update_time, + "estimated_mana": estimated_mana, "estimated_max": estimated_max, "estimated_pct": estimated_pct.toFixed(2),"fullin":timeTilFullPower(estimated_pct*100)}; + console.log(res); + return res; + + //}); + } + + //function handles claiming spots for accounts + async function claimDiscountedAccount(){ + const claim_op = [ + 'claim_account', + { + creator: config.account, + fee: '0.000 STEEM', + extensions: [], + } + ]; + const ops = [claim_op]; + const privateKey = dsteem.PrivateKey.fromString( + config.active_key + ); + await client.broadcast.sendOperations(ops, privateKey).then( + function(result) { + console.log(result); + console.log('>>claimed discounted account spot'); + }, + function(error){ + console.log(error); + } + ); + } + function getVoteRShares(voteWeight, account, power) { if (!account) { return; @@ -194,6 +252,7 @@ function format(n, c, d, t) { // with 100 being the max 100% per single vote, and 1,000 being the max potentially used votes // so if we were to only consume 10 % of our VP, the weight would be set at 50,000 instead of default value of 100,000 function calculateVotes(posts, weight) { + console.log('calculateVotes'); if(typeof weight == 'undefined') { weight = 100000; } From 6858f5e6e2db977e444ca4e8abce2147e1240c28 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Oct 2018 18:16:22 +0300 Subject: [PATCH 042/193] Add missed change for RC and Account Claim commit --- utils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils.js b/utils.js index 0a6dc55..2e9cf8a 100644 --- a/utils.js +++ b/utils.js @@ -393,6 +393,8 @@ function format(n, c, d, t) { module.exports = { getVotingPower: getVotingPower, + getRC: getRC, + claimDiscountedAccount: claimDiscountedAccount, getVoteValue: getVoteValue, timeTilFullPower: timeTilFullPower, getVestingShares: getVestingShares, From 1e91e9d33a56c3afa646fc903cad89a0cf40659d Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 18 Oct 2018 12:09:10 +0300 Subject: [PATCH 043/193] Add New API Endpoints - Implement new end point for retrieving total number of tokens distributed (by reward type but also full total) - Implement new end point for retrieving the token rewards per post --- app.js | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/app.js b/app.js index 23c1ed6..fba5245 100644 --- a/app.js +++ b/app.js @@ -147,6 +147,49 @@ app.get('/delegationPayments', async function(req, res) { }); +/* end point for returning total payments (categorized by reward type as well as a full total) on a specific date */ +app.get('/totalTokensDistributed', async function(req, res) { + + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + + await db.collection('token_transactions').aggregate([ + { + $match: + { + "date": { + "$lte": new Date(endDate), + "$gt": new Date(startDate) + } + } + }, + { + $group: + { + _id: {reward_activity:"$reward_activity"}, + tokens_distributed: { $sum: "$token_count" }, + } + } + ]).toArray(function(err, results) { + res.header('Access-Control-Allow-Origin', '*'); + //also append total token count to the grouped display + let tot_tokens = 0; + for (let entry of results) { + tot_tokens += entry.tokens_distributed; + } + console.log(tot_tokens); + results.push([{"_id":null,"tokens_distributed":tot_tokens}]); + + res.send(results); + console.log(results); + }); + +}); + /* end point for returning count of posts/activities rewarded */ app.get('/rewarded-activity-count', async function(req, res) { @@ -472,4 +515,38 @@ app.get('/getRank/:user', async function (req, res) { } }); + +/* end point for getting a post's reward */ +app.get('/getPostReward', async function (req, res) { + + if (typeof req.query.user!= "undefined" && req.query.user!=null + && typeof req.query.url!= "undefined" && req.query.url!=null){ + var user = req.query.user; + var url = req.query.url; + console.log('url:'+url); + //default query + var query_json = { + "reward_activity": "Post", + "user": user, + "url":url + }; + + let post_details = await db.collection('token_transactions').findOne(query_json, {fields : { _id:0} }); + console.log(post_details); + //fixing token amount display for 3 digits + if (typeof post_details!= "undefined" && post_details!=null){ + if (typeof post_details.token_count!= "undefined"){ + res.header('Access-Control-Allow-Origin', '*'); + res.send({token_count: post_details.token_count}); + } + }else{ + res.header('Access-Control-Allow-Origin', '*'); + res.send({token_count: 0}); + } + }else{ + res.header('Access-Control-Allow-Origin', '*'); + res.send({token_count: 0}); + } +}); + app.listen(process.env.PORT || 3000); From 1b4abf5be13f6e3aa430ea4332c473e23420f017 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 18 Oct 2018 12:17:47 +0300 Subject: [PATCH 044/193] Store Proper DB Date Format - Implement change to store date values as dates instead of strings (fix sorting of transactions for user display along with running a one time script to fix all 800K+ stored transactions) - Update APIs to function properly with new date format --- app.js | 50 +++++++++++++++++++++++++++++++------------------ curation-bot.js | 6 +++--- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/app.js b/app.js index fba5245..ccca58b 100644 --- a/app.js +++ b/app.js @@ -297,16 +297,24 @@ app.get('/reblogCount', async function (req, res) { /* end point for counting number of upvotes on a certain date param (default current date) */ app.get('/upvoteCount', async function (req, res) { - var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - //fileName = "steemrewards"+fileName+".json"; - var dateRegex = new RegExp ('^'+todayDate); // /^2018-08-05/ + + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); if (req.query.targetDate){ - dateRegex = new RegExp ('^'+req.query.targetDate); + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); } - let query = await db.collection('token_transactions').find({ + var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + //adjust query to include dates + query_json = { "reward_activity": "Post Vote", - "date": dateRegex - }) + "date": { + "$lte": new Date(endDate), + "$gt": new Date(startDate) + } + }; + + let query = await db.collection('token_transactions').find(query_json); + try{ console.log('counting'); let upvote_count = await query.count(); @@ -320,16 +328,24 @@ app.get('/upvoteCount', async function (req, res) { /* end point for counting number of rewarded posts on a certain date param (default current date) */ app.get('/rewardedPostCount', async function (req, res) { - var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - //fileName = "steemrewards"+fileName+".json"; - var dateRegex = new RegExp ('^'+todayDate); // /^2018-08-05/ + + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); if (req.query.targetDate){ - dateRegex = new RegExp ('^'+req.query.targetDate); + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); } - let query = await db.collection('token_transactions').find({ + var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + //adjust query to include dates + query_json = { "reward_activity": "Post", - "date": dateRegex - }) + "date": { + "$lte": new Date(endDate), + "$gt": new Date(startDate) + } + }; + + let query = await db.collection('token_transactions').find(query_json); + try{ console.log('counting'); let rewarded_post_count = await query.count(); @@ -355,16 +371,14 @@ userRewardedPostCountFunc = async function(req, res){ //console.log("days:"+days); var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); var endDate = moment(moment().utc().startOf('date').subtract(days, 'days').toDate()).format('YYYY-MM-DD'); - var startDateRegex = new RegExp ('^'+startDate); // /^2018-08-05/ - var endDateRegex = new RegExp ('^'+endDate); // /^2018-08-05/ //console.log("startDate:"+startDate+" endDate:"+endDate); //adjust query to include dates query_json = { "reward_activity": "Post", "user": user, "date": { - "$gte": endDate, - "$lt": startDate + "$gte": new Date(endDate), + "$lt": new Date(startDate) } }; } diff --git a/curation-bot.js b/curation-bot.js index 264aaf5..f983a81 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -524,7 +524,7 @@ function processVotes(query, subsequent) { token_count: parseInt(config.moderator_comment_reward), url: post.url, comment_url: comments[cmt_it].url, - date: comments[cmt_it].created + date: new Date(comments[cmt_it].created) } bulk_transactions.find( { @@ -615,7 +615,7 @@ function processVotes(query, subsequent) { reward_activity: activity_type, token_count: post.post_score, url: post.url, - date: post.created, + date: new Date(post.created), note: note, reward_system: reward_sys_version } @@ -650,7 +650,7 @@ function processVotes(query, subsequent) { reward_activity: 'Post Vote', token_count: voter_tokens, url: post.url, - date: vote.time + date: new Date(vote.time) } bulk_transactions.find( { From 030608186db8e415f1098cc87d34e8b61852f379 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 18 Oct 2018 12:20:30 +0300 Subject: [PATCH 045/193] Process more than 1 post per day per user - Implement a change to allow accepting more than 1 post per day (up to max 2) to overcome server time difference limitation. --- curation-bot.js | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index f983a81..9d7ff35 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -566,30 +566,39 @@ function processVotes(query, subsequent) { continue; } + //due to the difference in server times, a user's post might have same date created. + //to avoid this issue, we will accept 2 posts for every user + //so we will check if 2 posts are already accumulated for the user, and if so reject the third let last_index = _.findLastIndex(votePosts, ['author', post.author]); - if (last_index != -1) { - console.log('---- User already has post same date ------'); + let first_index = _.findIndex(votePosts, ['author', post.author]); + + if (last_index != -1 && (first_index!=last_index)) { + console.log('---- User already has more than 2 posts in 24 hours ------'); let last_voted = votePosts[last_index]; var last_date = moment(last_voted.created).format('D'); + let first_voted = votePosts[first_index]; + var first_date = moment(first_voted.created).format('D'); var this_date = moment(post.created).format('D'); - if (last_date != this_date) { - console.log('Voting on: ' + post.url); - votePosts.push(post); - } else { + //if all 3 dates match, skip it + if ((last_date == this_date) && (first_date == this_date)) { console.log('---- Last voted -----'); console.log(new Date (last_voted.created)); + console.log('---- First voted -----'); + console.log(new Date (first_voted.created)); console.log('---- This voted -----'); console.log(new Date (post.created)); console.log('---- Moment-----'); console.log(last_date); + console.log(first_date); console.log(this_date); continue; } - } else { - //console.log('Voting on: ' + post.url); - votePosts.push(post); } + + console.log('Voting on: ' + post.url); + votePosts.push(post); + try{ console.log('going through '+post.url); //insert post if not inserted before From f318d7ab8b3fae08acf85f9728496c0f99481199 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 18 Oct 2018 12:27:15 +0300 Subject: [PATCH 046/193] Allow swapping Steem payments for AFIT tokens - Implement bot functionality to track beneficiary payments to a specific actifit account, which would allow users to get fully paid in AFIT tokens after payout. --- delegations.js | 174 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 2 deletions(-) diff --git a/delegations.js b/delegations.js index 3b44d86..a6415d1 100644 --- a/delegations.js +++ b/delegations.js @@ -21,6 +21,10 @@ let properties let totalVests let totalSteem +let steemPrice = 1; +let sbdPrice = 1; +let newestTxId = -1; + console.log('--- Reward script initialized ---'); var schedule = require('node-schedule') @@ -36,10 +40,14 @@ var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ runRewards(true); function runRewards(steemOnlyReward){ + let mongo_conn = config.mongo_uri + if (config.testing){ + mongo_conn = config.mongo_local + } // Use connect method to connect to the server - MongoClient.connect(config.mongo_uri, async function (err, dbClient) { + MongoClient.connect(mongo_conn, async function (err, dbClient) { if (!err) { - console.log('Connected successfully to server: ' + config.mongo_uri) + console.log('Connected successfully to server: ' + mongo_conn) db = dbClient.db(dbName) // Get the documents collection @@ -49,6 +57,9 @@ function runRewards(steemOnlyReward){ var days = 1; startProcess(days, steemOnlyReward); + //grab steem prices and proceed checking for beneficiary payouts to AFIT token reward account (full_pay_benef_account) + setInterval(loadSteemPrices,5 * 60 * 1000); + } else { utils.log(err, 'delegations') @@ -65,6 +76,165 @@ function runRewards(steemOnlyReward){ }) } +//function to grab latest payouts for beneficiaries and reward with AFIT tokens +async function getBenefactorPosts (account, start, end) { + + //connect to the token_transactions table to start transactions to users + var bulk_transactions = db.collection('token_transactions').initializeUnorderedBulkOp(); + + let totalSBD = 0 + let totalSp = 0 + let limit = 2000; + let txStart = -1; + + start = moment(start).format() + end = moment(end).format() + console.log(start) + console.log(end) + + //grab current AFIT price in USD + let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next() + console.log('curAfitPrice:'+curAFITPrice.unit_price_usd); + + // Query account history for delegations + properties = await client.database.getDynamicGlobalProperties() + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) + totalVests = Number(properties.total_vesting_shares.split(' ')[0]) + //console.log(properties); + const transactions = await client.database.call('get_account_history', [account, txStart, limit]) + transactions.reverse() + let foundTx = false; + console.log("newestTxId:"+newestTxId); + let counter = 0; + for (let txs of transactions) { + if (counter == 0){ + counter += 1; + //if this is not our first run, start from where we left + if (newestTxId==txs[0]){ + //no new transactions, bail + console.log('we already went through last transaction'); + break; + }else{ + newestTxId = txs[0]; + console.log('set newestTxId:'+newestTxId); + } + } + let date = moment(txs[1].timestamp).format() + + if (date >= start && date <= end) { + console.log(txs[0]); + let op = txs[1].op + // Look for beneficiary payments + if (op[0] === 'comment_benefactor_reward') { + foundTx = true; + console.log('---------------------------------------'); + //console.log(op); + let matchingAFIT = 0; + console.log(op[1]); + let rewardedSP = parseFloat(vestsToSteemPower(op[1].vesting_payout).toFixed(3)) + console.log("rewardedSP:"+rewardedSP); + //calculate dollar value + let steemInUSD = rewardedSP * steemPrice; + console.log("steemInUSD:"+steemInUSD); + + //convert to AFIT and add to total + matchingAFIT = steemInUSD / curAFITPrice.unit_price_usd; + console.log("matchingAFIT:"+matchingAFIT); + + let rewardedSBD = parseFloat(op[1].sbd_payout.split(' ')[0]) + + console.log("rewardedSBD:"+rewardedSBD); + + //calculate dollar value + let sbdInUSD = rewardedSBD * sbdPrice; + console.log("sbdInUSD:"+sbdInUSD); + + //convert to AFIT and add to total + let sbdToAFIT = sbdInUSD / curAFITPrice.unit_price_usd; + matchingAFIT += sbdToAFIT; + + //format to 3 decimals + matchingAFIT = parseFloat(matchingAFIT.toFixed(3)); + + console.log("sbdToAFIT:"+sbdToAFIT); + + console.log("Total AFIT:"+matchingAFIT); + + let beneficSwapTansaction = { + user: op[1].author, + reward_activity: 'Full AFIT Payout', + url: op[1].permlink, + token_count: matchingAFIT, + orig_sbd_amount: rewardedSBD, + orig_steem_amount: rewardedSP, + date: new Date(date) + } + + //store this as a transaction + bulk_transactions.find( + { + user: beneficSwapTansaction.user, + reward_activity: beneficSwapTansaction.reward_activity, + url: beneficSwapTansaction.url + }).upsert().replaceOne(beneficSwapTansaction); + + } + } else if (date < start){ + break + } + } + //award transaction tokens + if (foundTx){ + bulk_transactions.execute(); + console.log('-- Processed Full AFIT Payouts --') + //once done, update user total token count + updateUserTokens(); + }else{ + console.log('-- No Posts to process --'); + } + +} + +//function to load relevant STEEM and SBD prices, and proceed with AFIT token swap/reward process +function loadSteemPrices() { + + console.log('-- start AFIT token swap process --') + + // Require the "request" library for making HTTP requests + var request = require("request"); + + // Load the price feed data + request.get('https://api.coinmarketcap.com/v1/ticker/steem/', function (e, r, data) { + try { + steemPrice = parseFloat(JSON.parse(data)[0].price_usd); + + console.log("Loaded STEEM price: " + steemPrice); + + // Load the price feed data + request.get('https://api.coinmarketcap.com/v1/ticker/steem-dollars/', function (e, r, data) { + try { + sbdPrice = parseFloat(JSON.parse(data)[0].price_usd); + + console.log("Loaded SBD price: " + sbdPrice); + + let days = 1; + let start = moment().utc().startOf('date').toDate() + let to = moment(start).subtract(days, 'days').toDate() + + //bring the action + getBenefactorPosts(config.full_pay_benef_account,to, start); + + } catch (err) { + console.log('Error loading SBD price: ' + err); + } + }); + + } catch (err) { + console.log('Error loading STEEM price: ' + err); + } + }); +} + async function startProcess (days, steemOnlyReward) { let end = 0 From df463b00b775caad8e4cf1c432ea9226634e02bb Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 18 Oct 2018 12:31:32 +0300 Subject: [PATCH 047/193] Implement delegation payout process changes - Update delegators payment to include SBD payouts - Update user token count right after delegation pay --- delegations.js | 55 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/delegations.js b/delegations.js index a6415d1..c08da06 100644 --- a/delegations.js +++ b/delegations.js @@ -1,5 +1,6 @@ const dsteem = require('dsteem') const client = new dsteem.Client('https://api.steemit.com') +//const client = new dsteem.Client('https://steemd.privex.io') const _ = require('lodash') const moment = require('moment') const utils = require('./utils') @@ -53,6 +54,9 @@ function runRewards(steemOnlyReward){ // Get the documents collection collection = db.collection(collectionName) + //updateUserTokens(); + //return; + //run for one day var days = 1; startProcess(days, steemOnlyReward); @@ -240,6 +244,7 @@ async function startProcess (days, steemOnlyReward) { let end = 0 // Find last saved delegation transaction let lastTx = await collection.find().sort({'tx_number': -1}).limit(1).next() + console.log('last recorded delegation transaction'); console.log(lastTx) if (lastTx) end = lastTx.tx_number await updateProperties() @@ -251,6 +256,8 @@ async function startProcess (days, steemOnlyReward) { if (!steemOnlyReward){ console.log('processTokenRewards'); await processTokenRewards(start, txEnd, days) + //update our user token count post reward + updateUserTokens(); } var d = new Date(); var dayId = d.getDay(); @@ -317,9 +324,11 @@ async function processSteemRewards (start) { const from = moment(to).subtract(7, 'days').toDate() Promise.all([getAcumulatedSteemPower(from, to, true), getBenefactorRewards(to, start, -1)]).then(values => { const activeDelegations = values[0].users - const steemRewards = values[1] + const steemRewards = values[1].split(' ')[0] + const sbdRewards = values[1].split(' ')[1] const totalDelegatedSteem = values[0].totalSteem const rewardPerSteem = steemRewards / totalDelegatedSteem + const rewardPerSBD = sbdRewards / totalDelegatedSteem const rewards = _.map(activeDelegations, function (o) { //skip opt out users from reward var user_opted_out = false; @@ -335,7 +344,8 @@ async function processSteemRewards (start) { if (!user_opted_out){ reward = { user: o.user, - steem: +(o.totalSteem * rewardPerSteem).toFixed(3) + steem: +(o.totalSteem * rewardPerSteem).toFixed(3), + sbd: +(o.totalSteem * rewardPerSBD).toFixed(3) } } @@ -350,9 +360,11 @@ async function processSteemRewards (start) { }) console.log(rewards) console.log("steem total beneficiary reward:"+steemRewards) + console.log("SBD total beneficiary reward:"+sbdRewards) const data = { rewards: rewards, - total: steemRewards, + totalSteem: steemRewards, + totalSBD: sbdRewards, totalUsers: rewards.length } @@ -430,7 +442,8 @@ async function processDelegations (account, start, end) { } } -async function getBenefactorRewards (start, end, txStart, totalSp) { +async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { + if (!totalSBD) totalSBD = 0 if (!totalSp) totalSp = 0 let limit = (txStart < 0) ? 10000 : Math.min(txStart, 10000) start = moment(start).format() @@ -449,9 +462,11 @@ async function getBenefactorRewards (start, end, txStart, totalSp) { let op = txs[1].op // Look for delegation operations if (op[0] === 'comment_benefactor_reward') { - //console.log(op[1]); + console.log(op[1]); let newSp = vestsToSteemPower(op[1].vesting_payout) totalSp = totalSp + newSp + let newSBD = op[1].sbd_payout.split(' ')[0] + totalSBD += parseFloat(newSBD) } } else if (date < start) break } @@ -463,7 +478,7 @@ async function getBenefactorRewards (start, end, txStart, totalSp) { console.log('-- Processed rewards ---') // console.log(totalSp.toFixed(3)) - return +totalSp.toFixed(3) + return +totalSp.toFixed(3)+' ' +totalSBD.toFixed(3) } async function getActiveDelegations (start, excludeOn) { @@ -649,3 +664,31 @@ async function updateProperties () { totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) totalVests = Number(properties.total_vesting_shares.split(' ')[0]) } + +//function handles updating current user token count +async function updateUserTokens() { + console.log('---- Updating User Tokens ----'); + + try{ + //group all token transactions per user, and sum them to generate new total count + let query = await db.collection('token_transactions').aggregate([ + { $group: { _id: "$user", tokens: { $sum: "$token_count" } } }, + { $sort: { tokens: -1 } }, + { $project: { + _id: "$_id", + user: "$_id", + tokens: "$tokens", + } + } + ]) + + let user_tokens = await query.toArray(); + //remove old token count per user + await db.collection('user_tokens').remove({}); + //insert new count per user + await db.collection('user_tokens').insert(user_tokens); + console.log('---- Updating User Tokens Complete ----'); + }catch(err){ + console.log('>>save data error:'+err.message); + } +} From 3c53c22b59bc88eac1256cf450d15d0f1c7759a6 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 31 Oct 2018 00:32:41 +0200 Subject: [PATCH 048/193] Implement Standard Improvements - increase standard limit of transactions to 1,000 - adjust params to common functions to remove useless data being sent - factor out moderator list fetching function to allow better code reusability --- app.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index ccca58b..ef75697 100644 --- a/app.js +++ b/app.js @@ -80,7 +80,7 @@ app.get('/transactions/:user?', async function (req, res) { transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); }else{ //only limit returned transactions in case this is a general query - transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(250).toArray(); + transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); } res.header('Access-Control-Allow-Origin', '*'); res.send(transactions); @@ -222,23 +222,28 @@ app.get('/topDelegators', async function (req, res) { res.send(delegatorList); }); -activeDelegationFunc = async function (req, res){ - let user = await db.collection('active_delegations').findOne({_id: req.params.user}, {fields : { _id:0} }); +activeDelegationFunc = async function (userName){ + let user = await db.collection('active_delegations').findOne({_id: userName}, {fields : { _id:0} }); console.log(user); return user; } /* end point for returning a single user last recorded active delegation amount */ app.get('/delegation/:user', async function (req, res) { - var user = await activeDelegationFunc(req, res); + var user = await activeDelegationFunc(req.params.user); res.header('Access-Control-Allow-Origin', '*'); res.send(user); }); +moderatorsListFunc = async function () { + let moderatorList = await db.collection('team').find({title:'moderator', status:'active'}).sort({name: 1}).toArray(); + return moderatorList; +} + /* end point for returning current active moderators data by actifit */ app.get('/moderators', async function (req, res) { var moderatorList; - moderatorList = await db.collection('team').find({title:'moderator', status:'active'}).sort({name: 1}).toArray(); + moderatorList = await moderatorsListFunc(); res.header('Access-Control-Allow-Origin', '*'); res.send(moderatorList); }); From e733620af936178ee43bf41452ba0a813ef085ed Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 31 Oct 2018 00:33:45 +0200 Subject: [PATCH 049/193] Implement Moderator Activity Endpoint - implement new end point for fetching moderator activity while allowing optional filters by date and date range --- app.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/app.js b/app.js index ef75697..161faef 100644 --- a/app.js +++ b/app.js @@ -568,4 +568,63 @@ app.get('/getPostReward', async function (req, res) { } }); +/* end point for capturing moderator activity on a specific date and for a specific period (defaults today and a single day activity) */ +app.get('/moderatorActivity', async function(req, res) { + let moderatorsList = await moderatorsListFunc(); + + //default today + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + //default single day + let days = 1; + if (!isNaN(req.query.days)){ + days = req.query.days; + } + var endDate = moment(moment(startDate).utc().subtract(days, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + + await db.collection('team').aggregate([ + { + $match: + { + title:'moderator', + status:'active' + } + }, + { + $lookup: + { + from: "token_transactions", + localField: "name", + foreignField: "user", + as: "moderatorActivity" + } + }, + { + $project: + { + '_id':0, + items: + { + $filter: { + input: "$moderatorActivity", + as: "singleEntry", + cond: { $and: [ + { "$lte": ["$$singleEntry.date", new Date(startDate)] }, + { "$gt": ["$$singleEntry.date", new Date(endDate)] } + ] } + } + } + } + } + ]).toArray(function(err, results) { + res.header('Access-Control-Allow-Origin', '*'); + res.send(results); + console.log(results); + }); + +}); + app.listen(process.env.PORT || 3000); From ff2cd6b0a4ba55466441a0b06622c894ced71846 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 31 Oct 2018 00:41:15 +0200 Subject: [PATCH 050/193] New delegator beneficiary account feature - implement new functionality to allow directing delegator rewards to alt accounts in terms of User Rank, delegation rewarded AFIT and delegation rewarded STEEM/SBD --- app.js | 82 ++++++++++++++++++++++++++++++++++++++++++++++++-- delegations.js | 42 ++++++++++++++++++++++++-- 2 files changed, 119 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 161faef..c0b6a79 100644 --- a/app.js +++ b/app.js @@ -479,12 +479,44 @@ app.get('/getRank/:user', async function (req, res) { var user_rank = 0; //grab delegation amount - var userDelegations = await activeDelegationFunc(req, res); + var userDelegations = await activeDelegationFunc(req.params.user); + + let delegSP = 0; + //get current delegated SP if any + if (userDelegations != null){ + console.log('already delegated'); + delegSP = userDelegations.steem_power; + } //console.log(userDelegations.steem_power); var delegation_score = 0; + + //check if the user has an alt account as beneficiary + let delegator_info = await getAltAccountStatusFunc(req.params.user); + //check if returned object is not empty + if (Object.keys(delegator_info).length > 0){ + if (parseInt(delegator_info.user_rank_benefit) == 1){ + //consider as no delegations + delegSP = 0; + } + }else{ + //also check the other case where the account is an alt-account + delegator_info = await getAltAccountByNameFunc(req.params.user); + //check if returned object is not empty + if (Object.keys(delegator_info).length > 0){ + if (parseInt(delegator_info.user_rank_benefit) == 1){ + //get original user delegation amount + userDelegations = await activeDelegationFunc(delegator_info.delegator); if (userDelegations != null){ - delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(userDelegations.steem_power)); + delegSP = userDelegations.steem_power; + } + } + } + } + + + if (parseFloat(delegSP) > 0){ + delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(delegSP)); } user_rank += delegation_score; @@ -534,6 +566,52 @@ app.get('/getRank/:user', async function (req, res) { } }); +/* function handles the backbone for grabbing Alt Account Status */ +getAltAccountStatusFunc = async function (user){ + let delegator_info = null; + if (typeof user!= "undefined" && user!=null){ + //in this case, we check the status of a single user + var query_json = { + "delegator": user + }; + + delegator_info = await db.collection('delegation_alt_beneficiaries').findOne(query_json, {fields : { _id:0} }); + if (delegator_info==null){ + delegator_info = {}; + } + console.log(delegator_info); + }else{ + //alternatively grab all alt-account reward delegations + delegator_info = await db.collection('delegation_alt_beneficiaries').find().toArray(); + console.log(delegator_info); + } + return delegator_info; +} + +/* function handles checking if alt-account is linked to a delegator */ +getAltAccountByNameFunc = async function (targetUser){ + let delegator_info = null; + if (typeof targetUser!= "undefined" && targetUser!=null){ + //in this case, we check the status of a single user + var query_json = { + "alt_account": targetUser + }; + + delegator_info = await db.collection('delegation_alt_beneficiaries').findOne(query_json, {fields : { _id:0} }); + if (delegator_info==null){ + delegator_info = {}; + } + console.log(delegator_info); + } + return delegator_info; +} + +/* end point for getting list of SP delegator accounts who wish to move their user rank and/or rewards to their alt-accounts*/ +app.get('/getAltAccountStatus/:user?', async function (req, res) { + let delegator_info = await getAltAccountStatusFunc(req.params.user); + res.header('Access-Control-Allow-Origin', '*'); + res.send(delegator_info); +}); /* end point for getting a post's reward */ app.get('/getPostReward', async function (req, res) { diff --git a/delegations.js b/delegations.js index c08da06..7bb19eb 100644 --- a/delegations.js +++ b/delegations.js @@ -287,8 +287,14 @@ async function processTokenRewards (start, end, days) { console.log(">>>>went beyond rewards limit. Apply multiplier"); } console.log(">>>>multiplier:"+multiplier); + + //load list of alt accounts to reward them instead of actual delegators + let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); + console.log(altAccounts); + //go through all delegators, and send out AFIT rewards for (let user of acumulatedSteemPower.users) { + //skip opt out users from reward var user_opted_out = false; for (var n = 0; n < config.exclude_rewards.length; n++) { @@ -301,10 +307,24 @@ async function processTokenRewards (start, end, days) { if (user_opted_out){ continue; } + + let reward_user = user.user; + let reward_activity = 'Delegation'; + + //check if this user has an alt account with delegated rewards enabled + let delegator_entry = _.find(altAccounts, {'delegator': user.user, 'reward_benefit': '1'}); + + //if so reward the alt account instead + if (delegator_entry != null) { + reward_user = delegator_entry.alt_account; + reward_activity += ' On Behalf'; + } + let reward = { - user: user.user, + user: reward_user, token_count: parseFloat((user.totalSteem * multiplier).toFixed(3)), - reward_activity: 'Delegation', + reward_activity: reward_activity, + orig_account: user.user, note: note, date: end } @@ -322,6 +342,11 @@ async function processSteemRewards (start) { console.log(config.pay_account) const to = moment(start).subtract(7, 'days').toDate() const from = moment(to).subtract(7, 'days').toDate() + + //load list of alt accounts to reward them instead of actual delegators + let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); + console.log(altAccounts); + Promise.all([getAcumulatedSteemPower(from, to, true), getBenefactorRewards(to, start, -1)]).then(values => { const activeDelegations = values[0].users const steemRewards = values[1].split(' ')[0] @@ -340,10 +365,21 @@ async function processSteemRewards (start) { } } + + let reward_user = o.user; + + //check if this user has an alt account with delegated rewards enabled + let delegator_entry = _.find(altAccounts, {'delegator': o.user, 'steem_reward_benefit': '1'}); + + //if so reward the alt account instead + if (delegator_entry != null) { + reward_user = delegator_entry.alt_account; + } + let reward = {}; if (!user_opted_out){ reward = { - user: o.user, + user: reward_user, steem: +(o.totalSteem * rewardPerSteem).toFixed(3), sbd: +(o.totalSteem * rewardPerSBD).toFixed(3) } From 3d0690394316f8eb77fb7a3b3b7b6229c622181e Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 31 Oct 2018 00:50:42 +0200 Subject: [PATCH 051/193] Append further comment meta data - Append further details to json meta data on actifit comments --- curation-bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/curation-bot.js b/curation-bot.js index 9d7ff35..be75f0a 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -894,7 +894,7 @@ function sendComment(post, vote_weight) { //replace(/\{milestone\}/g, milestone_txt). //adding proper meta content for later relevant reward via afit_tokens data - var jsonMetadata = { tags: ['actifit'], app: 'actifit/v'+version, user_rank: post.user_rank, content_score: post.content_score, media_score: post.media_score, upvote_score: post.upvote_score, comment_score: post.comment_score, user_rank_score: post.user_rank_score, afit_tokens: token_count, moderator_score: post.moderator_score, post_score: post.activity_score }; + var jsonMetadata = { tags: ['actifit'], app: 'actifit/v'+version, activity_count: post_step_count, user_rank: post.user_rank, content_score: post.content_score, media_score: post.media_score, upvote_score: post.upvote_score, comment_score: post.comment_score, user_rank_score: post.user_rank_score, moderator_score: post.moderator_score, post_activity_score: post.activity_score, afit_tokens: token_count, post_upvote: vote_weight }; if (!config.testing){ // Broadcast the comment steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { From 07b6b787b06853f9bb21b099e518a7bf6d23f5f7 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 9 Nov 2018 12:25:33 +0200 Subject: [PATCH 052/193] Add hourly constraint between reports implemented new constraint to prevent two reports from being voted if they have less than 6 hours difference. This is mostly to prevent issues with Steem BC timing out and sending incorrect response that post was not created, but also to circumvent potential abuse. --- curation-bot.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/curation-bot.js b/curation-bot.js index be75f0a..7655cb8 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -594,8 +594,25 @@ function processVotes(query, subsequent) { console.log(this_date); continue; } + }else if (last_index != -1){ + console.log('last_index:'+last_index); + console.log(post.author+post.url); + //adding condition to reject a post if a prior one exists that is less than 6 hours away + let last_voted = votePosts[last_index]; + //console.log(last_voted.author+last_voted.url); + var last_date = moment(last_voted.created).toDate(); + var this_date = moment(post.created).toDate(); + //check the hours difference + var hours_diff = Math.abs(this_date - last_date) / 36e5; + if (hours_diff Date: Fri, 9 Nov 2018 12:28:32 +0200 Subject: [PATCH 053/193] Implement Minor Fixes - Implement a fix to tackle number of retries issues upon voting - Implement a fix to skip end date target for fetching beneficiary rewards for full afit pay option --- curation-bot.js | 4 ++-- delegations.js | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 7655cb8..3ddc45e 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -765,7 +765,7 @@ function processVotes(query, subsequent) { var post_rank = 0; function votingProcess(posts, power_per_vote) { // Get the first bid in the list - sendVote(posts.pop(), 20, power_per_vote) + sendVote(posts.pop(), 0, power_per_vote) .then( res => { // If there are more posts, vote on the next one after 5 seconds if (posts.length > 0) { @@ -843,7 +843,7 @@ function sendVote(post, retries, power_per_vote) { utils.log(err, result); // Try again one time on error - if (retries < 1) + if (retries < 10) sendVote(post, retries + 1); else { var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' diff --git a/delegations.js b/delegations.js index 7bb19eb..3872f21 100644 --- a/delegations.js +++ b/delegations.js @@ -81,7 +81,7 @@ function runRewards(steemOnlyReward){ } //function to grab latest payouts for beneficiaries and reward with AFIT tokens -async function getBenefactorPosts (account, start, end) { +async function getBenefactorPosts (account, start) { //connect to the token_transactions table to start transactions to users var bulk_transactions = db.collection('token_transactions').initializeUnorderedBulkOp(); @@ -92,9 +92,8 @@ async function getBenefactorPosts (account, start, end) { let txStart = -1; start = moment(start).format() - end = moment(end).format() - console.log(start) - console.log(end) + + console.log('start date:'+start) //grab current AFIT price in USD let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next() @@ -125,7 +124,7 @@ async function getBenefactorPosts (account, start, end) { } let date = moment(txs[1].timestamp).format() - if (date >= start && date <= end) { + if (date >= start) { console.log(txs[0]); let op = txs[1].op // Look for beneficiary payments @@ -226,7 +225,7 @@ function loadSteemPrices() { let to = moment(start).subtract(days, 'days').toDate() //bring the action - getBenefactorPosts(config.full_pay_benef_account,to, start); + getBenefactorPosts(config.full_pay_benef_account, to); } catch (err) { console.log('Error loading SBD price: ' + err); @@ -248,7 +247,7 @@ async function startProcess (days, steemOnlyReward) { console.log(lastTx) if (lastTx) end = lastTx.tx_number await updateProperties() - if (!steemOnlyReward){ + if (!testRun){ await processDelegations(config.account, -1, end) } let start = moment().utc().startOf('date').subtract(days, 'days').toDate() @@ -257,8 +256,10 @@ async function startProcess (days, steemOnlyReward) { console.log('processTokenRewards'); await processTokenRewards(start, txEnd, days) //update our user token count post reward + if (!testRun){ updateUserTokens(); } + } var d = new Date(); var dayId = d.getDay(); // Check if today is Monday, to calculate steem rewards From d7b1e46efaa2171a27fcbd79e976b92bb4691314 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 9 Nov 2018 12:34:05 +0200 Subject: [PATCH 054/193] Implement Auto-rewards claims full AFIT Pay - Implemented a new functionality to automated beneficiary rewards claims for the full AFIT beneficiary account so that every hour a check is made if beneficiary rewards are available, and hence to claim them. --- delegations.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/delegations.js b/delegations.js index 3872f21..ef4fa99 100644 --- a/delegations.js +++ b/delegations.js @@ -64,6 +64,8 @@ function runRewards(steemOnlyReward){ //grab steem prices and proceed checking for beneficiary payouts to AFIT token reward account (full_pay_benef_account) setInterval(loadSteemPrices,5 * 60 * 1000); + //claim rewards once per hour + setInterval(claimRewards,60 * 60 * 1000); } else { utils.log(err, 'delegations') @@ -729,3 +731,44 @@ async function updateUserTokens() { console.log('>>save data error:'+err.message); } } +//function handles fetching account details for later use when claiming rewards +async function grabAccountDetails(){ + console.log('grabbing fund account details'); + let account = await client.database.call('get_accounts', [[config.full_pay_benef_account]]); + console.log(account); + return account[0]; +} +//function handles claiming pending account rewards +async function claimRewards(){ + //sign key properly to function with dsteem requirement + let privateKey = dsteem.PrivateKey.fromString( + config.full_pay_posting_key + ); + //fetch account details first to use correct values for claim + let funds_account = await grabAccountDetails(); + console.log(funds_account.reward_steem_balance); + console.log(funds_account.reward_sbd_balance); + console.log(funds_account.reward_vesting_balance); + //if we have any value to claim, proceed + if (parseFloat(funds_account.reward_steem_balance) > 0 || parseFloat(funds_account.reward_sbd_balance) > 0 || parseFloat(funds_account.reward_vesting_balance) > 0) { + const op = [ + 'claim_reward_balance', + { + account: config.full_pay_benef_account, + reward_steem: funds_account.reward_steem_balance.split(' ')[0] + ' STEEM', + reward_sbd: funds_account.reward_sbd_balance.split(' ')[0] + ' SBD', + reward_vests: funds_account.reward_vesting_balance.split(' ')[0] + ' VESTS', + }, + ]; + client.broadcast.sendOperations([op], privateKey).then( + function(result) { + console.log(result); + }, + function(error) { + console.log(error); + } + ) + }else{ + console.log('no rewards to claim for now'); + } +} From 9a097fc8635986418baf928a626da16325828cc3 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 9 Nov 2018 12:43:13 +0200 Subject: [PATCH 055/193] Create new API endpoints - Create new endpoint for retrieving a post's full AFIT Pay reward. This allows filtering based on user and post url - Create new endpoint for gathering total number of AFIT tokens paid out based on charity-based activity - Create new endpoint for retrieving number of AFIT tokens rewarded in return for STEEM/SBD based on Full AFIT Pay option - Refactor post reward functionality to allow code reuse across new end points. --- app.js | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 13 deletions(-) diff --git a/app.js b/app.js index c0b6a79..b912c46 100644 --- a/app.js +++ b/app.js @@ -473,7 +473,7 @@ app.get('/getRank/:user', async function (req, res) { [4,0.40], [6,0.60], [8,0.80], - [10,1] + [9,1] ] var user_rank = 0; @@ -613,17 +613,10 @@ app.get('/getAltAccountStatus/:user?', async function (req, res) { res.send(delegator_info); }); -/* end point for getting a post's reward */ -app.get('/getPostReward', async function (req, res) { - - if (typeof req.query.user!= "undefined" && req.query.user!=null - && typeof req.query.url!= "undefined" && req.query.url!=null){ - var user = req.query.user; - var url = req.query.url; - console.log('url:'+url); - //default query +/* function handles processing requests for getting AFIT token pay depending on reward activity type */ +getPostRewardFunc = async function(user, url, reward_activity){ var query_json = { - "reward_activity": "Post", + "reward_activity": reward_activity, "user": user, "url":url }; @@ -633,19 +626,106 @@ app.get('/getPostReward', async function (req, res) { //fixing token amount display for 3 digits if (typeof post_details!= "undefined" && post_details!=null){ if (typeof post_details.token_count!= "undefined"){ + return post_details.token_count; + } + } + //otherwise return no tokens + return 0; +} + +/* end point for getting a post's reward */ +app.get('/getPostReward', async function (req, res) { + + if (typeof req.query.user!= "undefined" && req.query.user!=null + && typeof req.query.url!= "undefined" && req.query.url!=null){ + var user = req.query.user; + var url = req.query.url; + console.log('url:'+url); + //grab specific reward type for user and post + var token_count = await getPostRewardFunc(user, url, "Post"); res.header('Access-Control-Allow-Origin', '*'); - res.send({token_count: post_details.token_count}); - } + res.send({token_count: token_count}); }else{ res.header('Access-Control-Allow-Origin', '*'); res.send({token_count: 0}); } +}); + +/* end point for retrieving a post's full AFIT Pay reward */ +app.get('/getPostFullAFITPayReward', async function (req, res) { + + if (typeof req.query.user!= "undefined" && req.query.user!=null + && typeof req.query.url!= "undefined" && req.query.url!=null){ + var user = req.query.user; + var url = req.query.url; + + //for the full AFIT rewards, grab only permalink portion without the community and author name + url = url.substring(url.lastIndexOf('/')+1); + console.log('url:'+url); + //grab specific reward type for user and post + var token_count = await getPostRewardFunc(user, url, "Full AFIT Payout"); + res.header('Access-Control-Allow-Origin', '*'); + res.send({token_count: token_count}); }else{ res.header('Access-Control-Allow-Origin', '*'); res.send({token_count: 0}); } }); + +/* end point for returning total number of rewarded tokens to charities based upon user activity, along with unique user count who donated */ +app.get('/getCharityRewards', async function(req, res) { + + await db.collection('token_transactions').aggregate([ + { + $match: {reward_activity:'Charity Post'} + }, + { + $group: + { + _id: null, + tokens_distributed: { $sum: "$token_count" }, + user_count: { $sum: 1 } + } + } + ]).toArray(function(err, results) { + var output = 'rewarded users:'+results[0].user_count+','; + output += 'tokens distributed:'+results[0].tokens_distributed; + res.header('Access-Control-Allow-Origin', '*'); + res.send(results); + console.log(results); + }); + +}); + + + +/* end point for returning total number of AFIT tokens paid in return for full AFIT pay along with matching STEEM + SBD */ +app.get('/getFullAFITPayStats', async function(req, res) { + + await db.collection('token_transactions').aggregate([ + { + $match: {"reward_activity": "Full AFIT Payout"} + }, + { + $group: + { + _id: null, + afit_tokens: { $sum: "$token_count" }, + orig_sbd_amount: { $sum: "$orig_sbd_amount" }, + orig_steem_amount: { $sum: "$orig_steem_amount" }, + transaction_count: { $sum: 1 } + } + } + ]).toArray(function(err, results) { + res.header('Access-Control-Allow-Origin', '*'); + res.send(results); + console.log(results); + }); + +}); + + /* end point for capturing moderator activity on a specific date and for a specific period (defaults today and a single day activity) */ app.get('/moderatorActivity', async function(req, res) { let moderatorsList = await moderatorsListFunc(); From 5c6bd4d5cab43a264ee179bc2511fc4744c1db5d Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 28 Dec 2018 15:35:15 +0200 Subject: [PATCH 056/193] Signup & referrals API (& other changes) - add new API end points for referrals and signups functionality - implement random lucky winner for double up rewards - add STEEM to benefactor calculation to more accurately calculate rewarded AFIT amount - add functionality to calculate daily earnings for actifit accounts --- app.js | 333 ++++++++++++++++++++++----- comment.md | 7 +- curation-bot.js | 584 +++++++++++++++++++++++++++--------------------- delegations.js | 125 ++++++----- utils.js | 403 +++++++++++++++++++++++++++++++-- 5 files changed, 1081 insertions(+), 371 deletions(-) diff --git a/app.js b/app.js index b912c46..cdf5e06 100644 --- a/app.js +++ b/app.js @@ -4,6 +4,7 @@ const MongoClient = require('mongodb').MongoClient; var utils = require('./utils'); const moment = require('moment') +var appPort = process.env.PORT || 3120; var app = express(); @@ -13,7 +14,10 @@ app.set('view engine', 'handlebars'); var config = utils.getConfig(); // Connection URL -const url = config.mongo_uri; +let url = config.mongo_uri; +if (config.testing){ + url = config.mongo_local; +} var db; var collection; @@ -36,6 +40,16 @@ MongoClient.connect(url, function(err, client) { }); +//allows setting acceptable origins to be included across all function calls +app.use(function(req, res, next) { + var allowedOrigins = ['*', 'https://actifit.io']; + var origin = req.headers.origin; + if(allowedOrigins.indexOf(origin) > -1){ + res.setHeader('Access-Control-Allow-Origin', origin); + } + return next(); +}); + app.get('/', function (req, res) { var data = {}; data.posts = [ @@ -50,6 +64,7 @@ app.get('/', function (req, res) { res.send('Hello there!'); }); + /* function handles calculating and returning user token count */ grabUserTokensFunc = async function (req, res){ let user = await collection.findOne({_id: req.params.user}, {fields : { _id:0} }); @@ -66,12 +81,10 @@ grabUserTokensFunc = async function (req, res){ /* end point for user total token count display */ app.get('/user/:user', async function (req, res) { let user = await grabUserTokensFunc(req,res); - res.header('Access-Control-Allow-Origin', '*'); res.send(user); }); - -/* end point for user transactions display (per user or general actifit token transactions, limited by 250 */ +/* end point for user transactions display (per user or general actifit token transactions, limited by 1000 */ app.get('/transactions/:user?', async function (req, res) { let query = {}; var transactions; @@ -82,12 +95,25 @@ app.get('/transactions/:user?', async function (req, res) { //only limit returned transactions in case this is a general query transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); } - res.header('Access-Control-Allow-Origin', '*'); res.send(transactions); }); +/* end point for user referrals display (per user or general referrals */ +app.get('/signups/:user?', async function (req, res) { + let query = {account_created: true}; + var referrals; + if(req.params.user){ + query['referrer'] = req.params.user; + referrals = await db.collection('signup_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + }else{ + //only limit returned referrals in case this is a general query + referrals = await db.collection('signup_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); + } + res.send(referrals); +}); + /* end point for returning number of awarded users and tokens distributed */ -app.get('/userTokensInfo', async function(req, res) { +app.get('/user-tokens-info', async function(req, res) { await db.collection(collection_name).aggregate([ { @@ -104,7 +130,6 @@ app.get('/userTokensInfo', async function(req, res) { ]).toArray(function(err, results) { var output = 'rewarded users:'+results[0].user_count+','; output += 'tokens distributed:'+results[0].tokens_distributed; - res.header('Access-Control-Allow-Origin', '*'); res.send(results); console.log(results); }); @@ -139,7 +164,6 @@ app.get('/delegationPayments', async function(req, res) { } } ]).toArray(function(err, results) { - res.header('Access-Control-Allow-Origin', '*'); res.send(results); console.log(results); }); @@ -175,7 +199,6 @@ app.get('/totalTokensDistributed', async function(req, res) { } } ]).toArray(function(err, results) { - res.header('Access-Control-Allow-Origin', '*'); //also append total token count to the grouped display let tot_tokens = 0; for (let entry of results) { @@ -198,7 +221,6 @@ app.get('/rewarded-activity-count', async function(req, res) { ]).toArray(function(err, results) { console.log(results); utils.log(results, 'rewarded-activity-count'); - res.header('Access-Control-Allow-Origin', '*'); res.send(results); }); }); @@ -206,7 +228,6 @@ app.get('/rewarded-activity-count', async function(req, res) { /* end point for returning charity data supported by actifit */ app.get('/charities', async function (req, res) { var charities = await db.collection('available_charities').find({status:"enabled"}, {charity_name: 1}).sort({charity_name: 1}).toArray(); - res.header('Access-Control-Allow-Origin', '*'); res.send(charities); }); @@ -218,7 +239,6 @@ app.get('/topDelegators', async function (req, res) { }else{ delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).limit(parseInt(req.query.count)).toArray(); } - res.header('Access-Control-Allow-Origin', '*'); res.send(delegatorList); }); @@ -231,7 +251,6 @@ activeDelegationFunc = async function (userName){ /* end point for returning a single user last recorded active delegation amount */ app.get('/delegation/:user', async function (req, res) { var user = await activeDelegationFunc(req.params.user); - res.header('Access-Control-Allow-Origin', '*'); res.send(user); }); @@ -244,7 +263,6 @@ moderatorsListFunc = async function () { app.get('/moderators', async function (req, res) { var moderatorList; moderatorList = await moderatorsListFunc(); - res.header('Access-Control-Allow-Origin', '*'); res.send(moderatorList); }); @@ -252,27 +270,36 @@ app.get('/moderators', async function (req, res) { app.get('/ambassadors', async function (req, res) { var ambassadorList; ambassadorList = await db.collection('team').find({title:'ambassador', status:'active'}).sort({name: 1}).toArray(); - res.header('Access-Control-Allow-Origin', '*'); res.send(ambassadorList); }); /* end point for returning current top AFIT token holders */ app.get('/topTokenHolders', async function (req, res) { - var delegatorList; + var tokenHolders; if (isNaN(req.query.count)){ - delegatorList = await db.collection('user_tokens').find().sort({tokens: -1}).toArray(); + tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).toArray(); }else{ - delegatorList = await db.collection('user_tokens').find().sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); + tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); } - res.header('Access-Control-Allow-Origin', '*'); - res.send(delegatorList); + let output = tokenHolders; + if (req.query.pretty){ + output = '#|Token Holder | AFIT Tokens Held |
'; + output += '|---|---|---|
'; + for(var i = 0; i < tokenHolders.length; i++) { + let tokenHolder = tokenHolders[i]; + output += (i+1) + '|'; + output += '@'+tokenHolder.user + '|'; + output += gk_add_commas(tokenHolder.tokens.toFixed(3)) + '|'; + output += '
'; + } + } + res.send(output); }); /* end point for returning accounts banned by actifit*/ -app.get('/bannedUsers', async function (req, res) { +app.get('/banned_users', async function (req, res) { var banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); - res.header('Access-Control-Allow-Origin', '*'); res.send(banned_users); }); @@ -293,7 +320,6 @@ app.get('/reblogCount', async function (req, res) { console.log('counting'); let reblog_count = await query.count(); console.log(reblog_count); - res.header('Access-Control-Allow-Origin', '*'); res.send(JSON.stringify({reblog_count:reblog_count})); }catch(err){ console.log(err.message); @@ -324,13 +350,13 @@ app.get('/upvoteCount', async function (req, res) { console.log('counting'); let upvote_count = await query.count(); console.log(upvote_count); - res.header('Access-Control-Allow-Origin', '*'); res.send(JSON.stringify({upvote_count:upvote_count})); }catch(err){ console.log(err.message); } }); + /* end point for counting number of rewarded posts on a certain date param (default current date) */ app.get('/rewardedPostCount', async function (req, res) { @@ -350,12 +376,11 @@ app.get('/rewardedPostCount', async function (req, res) { }; let query = await db.collection('token_transactions').find(query_json); - + try{ console.log('counting'); let rewarded_post_count = await query.count(); console.log(rewarded_post_count); - res.header('Access-Control-Allow-Origin', '*'); res.send(JSON.stringify({rewarded_post_count:rewarded_post_count})); }catch(err){ console.log(err.message); @@ -409,7 +434,6 @@ app.get('/userRewardedPostCount/:user', async function (req, res) { if (typeof req.params.user!= "undefined" && req.params.user!=null){ var rewarded_post_count = await userRewardedPostCountFunc(req, res); - res.header('Access-Control-Allow-Origin', '*'); res.send(JSON.stringify({rewarded_post_count:rewarded_post_count})); }else{ res.send(""); @@ -507,7 +531,7 @@ app.get('/getRank/:user', async function (req, res) { if (parseInt(delegator_info.user_rank_benefit) == 1){ //get original user delegation amount userDelegations = await activeDelegationFunc(delegator_info.delegator); - if (userDelegations != null){ + if (userDelegations != null){ delegSP = userDelegations.steem_power; } } @@ -559,7 +583,6 @@ app.get('/getRank/:user', async function (req, res) { }); console.log(score_components) - res.header('Access-Control-Allow-Origin', '*'); res.send(score_components); }else{ res.send(""); @@ -609,23 +632,22 @@ getAltAccountByNameFunc = async function (targetUser){ /* end point for getting list of SP delegator accounts who wish to move their user rank and/or rewards to their alt-accounts*/ app.get('/getAltAccountStatus/:user?', async function (req, res) { let delegator_info = await getAltAccountStatusFunc(req.params.user); - res.header('Access-Control-Allow-Origin', '*'); res.send(delegator_info); }); /* function handles processing requests for getting AFIT token pay depending on reward activity type */ getPostRewardFunc = async function(user, url, reward_activity){ - var query_json = { + var query_json = { "reward_activity": reward_activity, - "user": user, - "url":url - }; - - let post_details = await db.collection('token_transactions').findOne(query_json, {fields : { _id:0} }); - console.log(post_details); - //fixing token amount display for 3 digits - if (typeof post_details!= "undefined" && post_details!=null){ - if (typeof post_details.token_count!= "undefined"){ + "user": user, + "url":url + }; + + let post_details = await db.collection('token_transactions').findOne(query_json, {fields : { _id:0} }); + console.log(post_details); + //fixing token amount display for 3 digits + if (typeof post_details!= "undefined" && post_details!=null){ + if (typeof post_details.token_count!= "undefined"){ return post_details.token_count; } } @@ -643,12 +665,10 @@ app.get('/getPostReward', async function (req, res) { console.log('url:'+url); //grab specific reward type for user and post var token_count = await getPostRewardFunc(user, url, "Post"); - res.header('Access-Control-Allow-Origin', '*'); res.send({token_count: token_count}); - }else{ - res.header('Access-Control-Allow-Origin', '*'); - res.send({token_count: 0}); - } + }else{ + res.send({token_count: 0}); + } }); /* end point for retrieving a post's full AFIT Pay reward */ @@ -664,10 +684,8 @@ app.get('/getPostFullAFITPayReward', async function (req, res) { console.log('url:'+url); //grab specific reward type for user and post var token_count = await getPostRewardFunc(user, url, "Full AFIT Payout"); - res.header('Access-Control-Allow-Origin', '*'); res.send({token_count: token_count}); }else{ - res.header('Access-Control-Allow-Origin', '*'); res.send({token_count: 0}); } }); @@ -691,7 +709,6 @@ app.get('/getCharityRewards', async function(req, res) { ]).toArray(function(err, results) { var output = 'rewarded users:'+results[0].user_count+','; output += 'tokens distributed:'+results[0].tokens_distributed; - res.header('Access-Control-Allow-Origin', '*'); res.send(results); console.log(results); }); @@ -714,11 +731,11 @@ app.get('/getFullAFITPayStats', async function(req, res) { afit_tokens: { $sum: "$token_count" }, orig_sbd_amount: { $sum: "$orig_sbd_amount" }, orig_steem_amount: { $sum: "$orig_steem_amount" }, + orig_sp_amount: { $sum: "$orig_sp_amount" }, transaction_count: { $sum: 1 } } } ]).toArray(function(err, results) { - res.header('Access-Control-Allow-Origin', '*'); res.send(results); console.log(results); }); @@ -778,11 +795,225 @@ app.get('/moderatorActivity', async function(req, res) { } } ]).toArray(function(err, results) { - res.header('Access-Control-Allow-Origin', '*'); res.send(results); console.log(results); }); }); -app.listen(process.env.PORT || 3000); +/* end point to grab current AFIT token price */ +app.get('/curAFITPrice', async function(req, res) { + let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next(); + console.log('curAfitPrice:'+curAFITPrice.unit_price_usd); + res.send(curAFITPrice); +}); + +/* handles the process of creating accounts*/ +proceedAccountCreation = async function (req){ + //let's create the account now + let accountCreated = false; + let transStored = false; + accountCreated = await utils.createAccount(req.query.new_account, req.query.new_pass); + if (accountCreated){ + transStored = await storeSignupTransaction(req); + //proceed only if a proper referrer was sent + if (typeof req.query.referrer != 'undefined' && req.query.referrer != 'undefined' && req.query.referrer != null){ + referralRewarded = await storeReferralReward(req); + } + } + console.log('account created:'+accountCreated); + return accountCreated; +} + +/* handles saving data related to signup to db */ +storeSignupTransaction = async function (req){ + console.log('reward new user'); + let result = false; + //setup new reward transaction for user + let new_transaction = { + account_name: req.query.new_account, + usd_invest: parseFloat(req.query.usd_invest), + steem_invest: parseFloat(req.query.steem_invest), + afit_reward: parseFloat(req.query.afit_reward), + memo: req.query.memo, + account_created: true, + payment_confirmed: true, + confirming_tx: req.query.confirming_tx, + date: new Date(), + } + + if (typeof req.query.referrer != 'undefined' && req.query.referrer != 'undefined' && req.query.referrer != null){ + new_transaction['referrer'] = req.query.referrer; + new_transaction['referrer_afit_reward'] = parseFloat(req.query.afit_reward * config.referrerBonus); + } + + if (typeof req.query.email != 'undefined' && req.query.email != 'undefined' && req.query.email != '' && req.query.email != null){ + new_transaction['email'] = req.query.email; + } + + //make sure we're not double storing referral + let query = { + account_name: req.query.new_account, + referrer: req.query.referrer, + }; + + try{ + let transaction = await db.collection('signup_transactions') + .replaceOne(query, new_transaction, { upsert: true }); + result = true; + }catch(e){ + console.log(e); + result = false; + } + + //also store this properly into user balance + + new_transaction = { + user: req.query.new_account, + reward_activity: 'Signup Reward', + token_count: parseFloat(req.query.afit_reward), + date: new Date(), + steem_invest: parseFloat(req.query.steem_invest), + usd_invest: parseFloat(req.query.usd_invest), + note: 'Successful Signup', + } + + //make sure we're not double rewarding user + query = { + user: req.query.new_account, + reward_activity: 'Signup Reward', + }; + + try{ + let transaction = db.collection('token_transactions') + .replaceOne(query, new_transaction, { upsert: true }); + result = true; + }catch(e){ + console.log(e); + result = false; + } + console.log(result); + return result; +} + + +/* function handles saving referral info and reward if the signup came through a referral */ +storeReferralReward = async function (req){ + console.log('reward referrer'); + //setup new reward transaction for user + let refRewarded = false; + let new_transaction = { + user: req.query.referrer, + reward_activity: 'Signup Referral', + token_count: parseFloat(req.query.afit_reward * config.referrerBonus), + date: new Date(), + referred: req.query.new_account, + note: 'Referral reward for signup of user '+req.query.new_account, + } + + //make sure we're not double rewarding user + let query = { + user: req.query.referrer, + reward_activity: 'Signup Referral', + referred: req.query.new_account, + }; + + try{ + let transaction = await db.collection('token_transactions') + .replaceOne(query, new_transaction, { upsert: true }); + refRewarded = true; + }catch(e){ + console.log(e); + } + + console.log('success'); + return refRewarded; +}; + +//function handles the process of confirming payment receipt, and then proceeds with account creation, reward and delegation +app.get('/confirmPayment', async function(req,res){ + if (req.query.confirm_payment_token != config.confirmPaymentToken){ + res.send('{}'); + }else{ + let paymentReceivedTx = ''; + let accountCreated = false; + let spToDelegate = 10; + //keeping request alive to avoid timeouts + let intID = setInterval(function(){ + res.write(' '); + }, 3000); + try{ + //first step is to ensure memo has not been tampered with, nor has it been claimed before + //to do that, let's try to find if any signup has been done using this memo + let memo_used = await db.collection('signup_transactions').findOne({memo: req.query.memo}); + console.log('memo_used:'+memo_used); + if (typeof memo_used == "undefined" || memo_used == null){ + paymentReceivedTx = await utils.confirmPaymentReceived(req); + console.log('>>>> got TX '+paymentReceivedTx); + if (paymentReceivedTx != ''){ + req.query.confirming_tx = paymentReceivedTx; + console.log(req.query); + try{ + accountCreated = await claimAndCreateAccount(req); + if (accountCreated){ + delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate); + } + }catch(e){ + console.log(e); + } + } + } + }catch(err){ + console.log(err); + } + //we're done, let's clear our running interval + clearInterval(intID); + //res.send({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated}); + res.write(JSON.stringify({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated})); + res.end(); + } +}); + +/* core function for discounted account claims and creation */ +claimAndCreateAccount = async function (req){ + let accountClaimed = false; + let accountCreated = false; + let results = ''; + try{ + results = await utils.getRC(config.account); + console.log('Current RC: ' + utils.format(results.estimated_pct) + '% '); + if (results.estimated_pct>50){ + //if we reached min threshold, claim more spots for discounted accounts + accountClaimed = await utils.claimDiscountedAccount(); + } + }catch(err){ + console.log('error grabbing RC'); + } + + console.log('discounted account claimed:'+accountClaimed); + //proceed creating account + try{ + accountCreated = await proceedAccountCreation(req); + }catch(err){ + console.log(err); + } + return accountCreated; + +}; + +function gk_add_commas(nStr) { + if (isNaN(nStr)){ + return nStr; + } + nStr += ''; + var x = nStr.split('.'); + var x1 = x[0]; + var x2 = x.length > 1 ? '.' + x[1] : ''; + var rgx = /(\d+)(\d{3})/; + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + ',' + '$2'); + } + return x1 + x2; +} + +app.listen(appPort); diff --git a/comment.md b/comment.md index 8287b3d..cded4e0 100644 --- a/comment.md +++ b/comment.md @@ -1,13 +1,18 @@ Congrats on providing **Proof of Activity** via your Actifit report! +{lucky_reward} You have accordingly been rewarded {token_count} AFIT tokens for your effort in reaching {step_count} activity, as well as your user rank and report quality! You also received an {weight}% upvote via @actifit account. -Actifit reward structure has changed recently, and the new rewards and upvotes are based on your: +Actifit rewards and upvotes are based on your: - User rank: which depends on your delegated SP, accumulated AFIT tokens, rewarded post count and recent rewarded activity. - Post score: which depends on your activity count, post content, post upvotes, quality comments, moderator review and user rank. + To improve your user rank, delegate more, pile up more AFIT tokens, and post more. To improve your post score, get to the max activity count, work on improving your post content, improve your user rank, engage with the community to get more upvotes and quality comments. +Actifit is now a Steem Witness. If you believe in our project, consider [voting for us](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=1) + ![rulersig2.jpg](https://cdn.steemitimages.com/DQmXrZz658YfMQBXNTA12rmbzqWXASfaGcNSqatJJ2ba7NR/rulersig2.jpg) +Vote for [Actifit as a Witness](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=1) Chat with us on [discord](https://discord.gg/aHtcA6r) | Visit our [website](https://actifit.io/) [Download on playstore](https://bit.ly/actifit-app) | [Download on app store](https://bit.ly/actifit-ios) [FAQs](https://steemit.com/actifit/@katerinaramm/actifit-app-or-rewarding-fitness-activity-with-tokens-and-steemit-upvotes-faqs) | [Text Tutorial](https://steemit.com/utopian-io/@katerinaramm/tutorial-for-actifit-app-android) | [Video Tutorial](https://youtu.be/tqkaDoonyvI) \ No newline at end of file diff --git a/curation-bot.js b/curation-bot.js index 3ddc45e..094ed59 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -21,12 +21,14 @@ var vote_time; var last_votes = Array(); var skip = false; var version = '0.3.4'; +var lucky_winner_id = -1; //version of the reward system var reward_sys_version = 'v0.2'; var error_sent = false; + var crypto = require('crypto'); const activity_rules = [ @@ -105,15 +107,17 @@ const actifit_img_urls = [ "https://cdn.steemitimages.com/DQmVD3pXR4EHzYeCapMNSanTeK9wGJeJ24XYJhZSUmjJReR/A-11.png", "https://cdn.steemitimages.com/DQmY67NW9SgDEsLo2nsAw4nYcddrTjp4aHNLyogKvGuVMMH/A-9.png", "https://cdn.steemitimages.com/DQmcrdacUAEHoeiX9gNVAiiL5iydmJoPve2nXpzszNtJZPb/A-12.png", "https://cdn.steemitimages.com/DQmbP8GuFvcHUyh7bKDheDN5iz8ERPCYzMaSVoRT2R5ZYPE/A-15.png","https://cdn.steemitimages.com/DQmW1VsUNbEjTUKawau4KJQ6agf41p69teEvdGAj1TMXmuc/A-5.png", "https://cdn.steemitimages.com/DQmeBn1PLf6a3QaXjM23EbQcaKtfDckgtGPHE4DApoUeBEJ/A-1.png", "https://cdn.steemitimages.com/DQmV7NRosGCmNLsyHGzmh4Vr1pQJuBPEy2rk3WvnEUDxDFA/A-21.png", "https://cdn.steemitimages.com/DQmdNAWWwv6MAJjiNUWRahmAqbFBPxrX8WLQvoKyVHHqih1/A-19.png","https://cdn.steemitimages.com/DQmVNqM8wQj2TnfwqSPYtfAuPHYjeBXSFekCHGZw9K3B9Gi/A-16.png", "https://cdn.steemitimages.com/DQma7nn1yV2w9iY6qXDBJUoTWkELTYxot7R9eoG1M3Tbtqn/A-7.png"]; -steem.api.setOptions({ url: 'https://api.steemit.com' });//https://gtg.steem.house:8090 -//steem.api.setOptions({ url: 'https://gtg.steem.house:8090' }); - -utils.log("* START - Version: " + version + " *"); // Load the settings from the config file loadConfig(); var botNames; +steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true +}); + +utils.log("* START - Version: " + version + " *"); // Connection URL var url = config.mongo_uri; @@ -127,6 +131,7 @@ var db; var collection; var db_name = config.db_name; + const collection_name = 'banned_accounts'; var banned_users; @@ -160,14 +165,14 @@ if (fs.existsSync('members.json')) { // Use connect method to connect to the server MongoClient.connect(url, function(err, client) { if(!err) { - console.log("Connected successfully to server"); + console.log("Connected successfully to server "+url); db = client.db(db_name); // Get the documents collection collection = db.collection(collection_name); //only start the process once we connected to the DB -startProcess(); + startProcess(); } else { utils.log(err, 'api'); } @@ -177,7 +182,7 @@ startProcess(); // Schedule to run every minute if (!config.testing){ -setInterval(startProcess, 60 * 1000); + setInterval(startProcess, 60 * 1000); }else{ setTimeout(startProcess, 20 * 1000); } @@ -240,8 +245,8 @@ async function startProcess() { } console.log('lets vote'); - skip = true; - + skip = true; + console.log('fetch banned users list'); //grab banned user list before rewarding banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); @@ -257,9 +262,9 @@ async function startProcess() { } console.log(moderator_list); - var query = {tag: config.main_tag, limit: 100}; - votePosts = Array(); - processVotes(query, false); + var query = {tag: config.main_tag, limit: 100}; + votePosts = Array(); + processVotes(query, false); }else{ //if we're not voting, let's check to claim some more discounted account spots utils.getRC(config.account).then(function(results){ @@ -267,13 +272,12 @@ async function startProcess() { if (results.estimated_pct>config.account_claim_rc_min){ //if we reached min threshold, claim more spots for discounted accounts utils.claimDiscountedAccount(); - } - + } }, function(err) { console.log("Error fetching RC"); console.log(err); }); - } + } } else if(skip) skip = false; @@ -284,13 +288,14 @@ async function startProcess() { //var post_scores = []; function processVotes(query, subsequent) { - + console.log('processVotes'); + steem.api.getDiscussionsByCreated(query, async function (err, result) { if (result && !err) { - is_voting = true; + is_voting = true; - utils.log(result.length + ' posts to process...'); - + utils.log(result.length + ' posts to process...'); + //initialize inserting posts to db var bulk = db.collection('posts').initializeUnorderedBulkOp(); @@ -298,129 +303,129 @@ function processVotes(query, subsequent) { //connect to the token_transactions table to start rewarding var bulk_transactions = db.collection('token_transactions').initializeUnorderedBulkOp(); - for(var i = 0; i < result.length; i++) { - var post = result[i]; - - //if this is a subsequent call, we need to skip first post - if (subsequent && i==0){ - // console.log('skip post:'+post.title); - //continue to next element - continue; - } - - //if this is the last post, save it to skip it in next iteration - if (i == result.length - 1){ - utils.log('storing last post iteration: ' + post.url); - //update query element to include the most recent post for a starting point of the next iteration - query['start_permlink'] = post.permlink; - query['start_author'] = post.author; - } + for(var i = 0; i < result.length; i++) { + var post = result[i]; - // Make sure the post is older than config time - if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { - utils.log('This post is too new for a vote: ' + post.url); - continue; - } + //if this is a subsequent call, we need to skip first post + if (subsequent && i==0){ + // console.log('skip post:'+post.title); + //continue to next element + continue; + } + + //if this is the last post, save it to skip it in next iteration + if (i == result.length - 1){ + utils.log('storing last post iteration: ' + post.url); + //update query element to include the most recent post for a starting point of the next iteration + query['start_permlink'] = post.permlink; + query['start_author'] = post.author; + } - // Check if the bot already voted on this post - if(post.active_votes.find(v => v.voter == 'actifit')) { - utils.log('Bot already voted on: ' + post.url); - continue; - } + // Make sure the post is older than config time + if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { + utils.log('This post is too new for a vote: ' + post.url); + continue; + } - // Check if any tags on this post are blacklisted in the settings - if ((config.blacklisted_tags && config.blacklisted_tags.length > 0) || (config.whitelisted_tags && config.whitelisted_tags.length > 0) && post.json_metadata && post.json_metadata != '') { - var tags = JSON.parse(post.json_metadata).tags; + // Check if the bot already voted on this post + if(post.active_votes.find(v => v.voter == 'actifit')) { + utils.log('Bot already voted on: ' + post.url); + continue; + } - if((config.blacklisted_tags && config.blacklisted_tags.length > 0) && tags && tags.length > 0 && tags.find(t => config.blacklisted_tags.indexOf(t) >= 0)) { - utils.log('Post contains one or more blacklisted tags. ' + post.url); - continue; - } + // Check if any tags on this post are blacklisted in the settings + if ((config.blacklisted_tags && config.blacklisted_tags.length > 0) || (config.whitelisted_tags && config.whitelisted_tags.length > 0) && post.json_metadata && post.json_metadata != '') { + var tags = JSON.parse(post.json_metadata).tags; - if((config.whitelisted_tags && config.whitelisted_tags.length > 0) && tags && tags.length > 0 && !tags.find(t => config.whitelisted_tags.indexOf(t) >= 0)) { - utils.log('Post does not contain a whitelisted tag. ' + post.url); - continue; - } - } + if((config.blacklisted_tags && config.blacklisted_tags.length > 0) && tags && tags.length > 0 && tags.find(t => config.blacklisted_tags.indexOf(t) >= 0)) { + utils.log('Post contains one or more blacklisted tags. ' + post.url); + continue; + } - // Check if post category is main tag - if (post.category != config.main_tag) { - utils.log('Post does not match category tag. ' + post.url); - continue; - } + if((config.whitelisted_tags && config.whitelisted_tags.length > 0) && tags && tags.length > 0 && !tags.find(t => config.whitelisted_tags.indexOf(t) >= 0)) { + utils.log('Post does not contain a whitelisted tag. ' + post.url); + continue; + } + } - // Check if this post has been flagged by any flag signal accounts - if(config.flag_signal_accounts) { - if(post.active_votes.find(function(v) { return v.percent < 0 && config.flag_signal_accounts.indexOf(v.voter) >= 0; })) { - utils.log('Post was downvoted by a flag signal account. ' + post.url); - continue; - } - } + // Check if post category is main tag + if (post.category != config.main_tag) { + utils.log('Post does not match category tag. ' + post.url); + continue; + } - // Check if this post has been voted by any type of paid bot - if(botNames && config.no_paid_bots) { - if(post.active_votes.find(function(v) { return botNames.includes(v.voter); })) { - utils.log('Post was vote by a paid bot account. ' + post.url); - continue; - } - } + // Check if this post has been flagged by any flag signal accounts + if(config.flag_signal_accounts) { + if(post.active_votes.find(function(v) { return v.percent < 0 && config.flag_signal_accounts.indexOf(v.voter) >= 0; })) { + utils.log('Post was downvoted by a flag signal account. ' + post.url); + continue; + } + } - // Check if account is beneficiary - var benefit = 0; - for (var x = 0; x < post.beneficiaries.length; x++) { - for (var n = 0; n < config.beneficiaries.length; n++) { - if (post.beneficiaries[x].account === config.beneficiaries[n]) - benefit ++; - } - if (benefit === config.beneficiaries.length) { - benefit = true; - break; - } - } - if (!benefit) { - utils.log('Post does not match account beneficiary. ' + post.url); - continue; - } - - //check if user is banned - var user_banned = false; - for (var n = 0; n < banned_users.length; n++) { - if (post.author == banned_users[n].user){ - utils.log('User '+post.author+' is banned, skipping his post:' + post.url); - user_banned = true; + // Check if this post has been voted by any type of paid bot + if(botNames && config.no_paid_bots) { + if(post.active_votes.find(function(v) { return botNames.includes(v.voter); })) { + utils.log('Post was vote by a paid bot account. ' + post.url); + continue; + } + } + + // Check if account is beneficiary + var benefit = 0; + for (var x = 0; x < post.beneficiaries.length; x++) { + for (var n = 0; n < config.beneficiaries.length; n++) { + if (post.beneficiaries[x].account === config.beneficiaries[n]) + benefit ++; + } + if (benefit === config.beneficiaries.length) { + benefit = true; break; + } + } + if (!benefit) { + utils.log('Post does not match account beneficiary. ' + post.url); + continue; } - } - if (user_banned) continue; - - //skip any posts that are more than 1.5 days old - if((new Date() - new Date(post.created + 'Z')) >= (config.max_days * 24 * 60 * 60 * 1000)) { - continue; - } - try { + //check if user is banned + var user_banned = false; + for (var n = 0; n < banned_users.length; n++) { + if (post.author == banned_users[n].user){ + utils.log('User '+post.author+' is banned, skipping his post:' + post.url); + user_banned = true; + break; + } + } + if (user_banned) continue; - post.json = JSON.parse(post.json_metadata); - - - //check if the post has an encryption key val, and ensure it is the proper one - if (post.json.actiCrVal){ - var txt_to_encr = post.author + post.permlink + post.json.step_count ; - var cipher = crypto.createCipher(config.encr_mode, config.encr_key); - let encr_txt = cipher.update(txt_to_encr, 'utf8', 'hex'); - encr_txt += cipher.final('hex'); - //test the result to the post's relevant data - if (post.json.actiCrVal != encr_txt){ - //wrong, skip post - console.log('post has incorrect actiCrVal'); + //skip any posts that are more than 1.5 days old + if((new Date() - new Date(post.created + 'Z')) >= (config.max_days * 24 * 60 * 60 * 1000)) { continue; - } - //console.log('post is valid'); - }else{ - console.log('post does not contain actiCrVal'); - continue; - } - + } + + try { + + post.json = JSON.parse(post.json_metadata); + + + //check if the post has an encryption key val, and ensure it is the proper one + if (post.json.actiCrVal){ + var txt_to_encr = post.author + post.permlink + post.json.step_count ; + var cipher = crypto.createCipher(config.encr_mode, config.encr_key); + let encr_txt = cipher.update(txt_to_encr, 'utf8', 'hex'); + encr_txt += cipher.final('hex'); + //test the result to the post's relevant data + if (post.json.actiCrVal != encr_txt){ + //wrong, skip post + console.log('post has incorrect actiCrVal'); + continue; + } + //console.log('post is valid'); + }else{ + console.log('post does not contain actiCrVal'); + continue; + } + /**************** Post Score calculation section *******************/ /******************* activity count criteria *********************/ @@ -565,35 +570,35 @@ function processVotes(query, subsequent) { console.log(err); continue; } - + //due to the difference in server times, a user's post might have same date created. //to avoid this issue, we will accept 2 posts for every user //so we will check if 2 posts are already accumulated for the user, and if so reject the third - - let last_index = _.findLastIndex(votePosts, ['author', post.author]); + + let last_index = _.findLastIndex(votePosts, ['author', post.author]); let first_index = _.findIndex(votePosts, ['author', post.author]); if (last_index != -1 && (first_index!=last_index)) { console.log('---- User already has more than 2 posts in 24 hours ------'); - let last_voted = votePosts[last_index]; - var last_date = moment(last_voted.created).format('D'); + let last_voted = votePosts[last_index]; + var last_date = moment(last_voted.created).format('D'); let first_voted = votePosts[first_index]; var first_date = moment(first_voted.created).format('D'); - var this_date = moment(post.created).format('D'); + var this_date = moment(post.created).format('D'); //if all 3 dates match, skip it if ((last_date == this_date) && (first_date == this_date)) { - console.log('---- Last voted -----'); - console.log(new Date (last_voted.created)); + console.log('---- Last voted -----'); + console.log(new Date (last_voted.created)); console.log('---- First voted -----'); console.log(new Date (first_voted.created)); - console.log('---- This voted -----'); - console.log(new Date (post.created)); - console.log('---- Moment-----'); - console.log(last_date); + console.log('---- This voted -----'); + console.log(new Date (post.created)); + console.log('---- Moment-----'); + console.log(last_date); console.log(first_date); - console.log(this_date); + console.log(this_date); continue; - } + } }else if (last_index != -1){ console.log('last_index:'+last_index); console.log(post.author+post.url); @@ -610,14 +615,14 @@ function processVotes(query, subsequent) { continue; } - } + } - console.log('Voting on: ' + post.url); + //console.log('Voting on: ' + post.url); votePosts.push(post); try{ - console.log('going through '+post.url); + console.log('going through selected post '+post.url); //insert post if not inserted before bulk.find( { permlink: post.permlink } ).upsert().replaceOne( post @@ -677,7 +682,7 @@ function processVotes(query, subsequent) { token_count: voter_tokens, url: post.url, date: new Date(vote.time) - } + } bulk_transactions.find( { user: vote_transaction.user, @@ -700,6 +705,10 @@ function processVotes(query, subsequent) { try{ //store posts await bulk.execute(); + }catch(bulkerr){ + console.log(bulkerr); + } + try{ //award transaction tokens bulk_transactions.execute(); }catch(bulkerr){ @@ -716,26 +725,84 @@ function processVotes(query, subsequent) { //call again with subsequent enabled to avoid duplicate posts, disparse the calls by 1 sec to avoid API timeouts console.log("query:"+query['tag']); console.log("query:"+query['start_permlink']); + setTimeout(processVotes, 1000, query, true); }else{ - - - if (votePosts.length > 0) { - utils.log(votePosts.length + ' posts to vote...'); + if (votePosts.length > 0) { + utils.log(votePosts.length + ' posts to vote...'); var vote_data = utils.calculateVotes(votePosts, config.vote_weight); - votePosts.sort(function(post1, post2) { + votePosts.sort(function(post1, post2) { //Sort posts by reverse score, so as when popping them we get sorted by highest return post1.post_score - post2.post_score; - }); - + }); + + + utils.log(vote_data.power_per_vote + ' power per full vote.'); + + + /************************* winner reward ******************************/ + + //let's pick a random winner to double up his votes and adjust his AFIT reward score + + try{ + lucky_winner_id = utils.generateRandomNumber(1, votePosts.length); + let post = votePosts[lucky_winner_id]; + + console.log('before'); + console.log(votePosts[lucky_winner_id].post_score); + + let reward_user = post.author; + let activity_type = 'Post'; + let note = ''; + let reward_factor = 2; + + //if we find this is a charity run, let's switch it to the actual charity name + if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ + reward_user = post.json.charity; + activity_type = 'Charity Post'; + note = 'Charity donation via actifit post by user '+post.author; + } + + var bulk_transactions = db.collection('token_transactions').initializeUnorderedBulkOp(); + + let post_transaction = { + user: reward_user, + reward_activity: activity_type, + token_count: post.post_score * reward_factor, + orig_token_count: post.post_score, + url: post.url, + date: new Date(post.created), + note: note, + lucky_winner: 1, + reward_factor: reward_factor, + reward_system: reward_sys_version + } + + //adjust post_score according to reward + post.post_score = post.post_score * reward_factor; + post.rate_multiplier = post.post_score / 100; + post.reward_factor = reward_factor; + post.lucky_winner = 1; + + bulk_transactions.find( + { + user: post_transaction.user, + reward_activity: post_transaction.reward_activity, + url: post_transaction.url + }).upsert().replaceOne(post_transaction); + + + //award transaction tokens + await bulk_transactions.execute(); + }catch(bulkerr){ + console.log(bulkerr); + } + console.log('after'); + console.log(votePosts[lucky_winner_id].post_score); + + /********************* proceed with STEEM upvotes ************************/ - utils.log(vote_data.power_per_vote + ' power per full vote.'); - /*utils.log(vote_data.power_per_vote * 0.8 + ' power per second vote.'); - utils.log(vote_data.power_per_vote * 0.65 + ' power per third vote.'); - utils.log(vote_data.power_per_vote * 0.5 + ' power per fourth vote.'); - utils.log(vote_data.power_per_vote * 0.35 + ' power per fifth vote.'); - utils.log(vote_data.power_per_vote * 0.2 + ' power per lowest vote.');*/ var tot_weight = 0; for (var xx=0;xx { // If there are more posts, vote on the next one after 5 seconds if (posts.length > 0) { - setTimeout(function () { votingProcess(posts, power_per_vote); }, 3000); + setTimeout(function () { votingProcess(posts, power_per_vote); }, 5000); } else { post_rank = 0; setTimeout(function () { @@ -791,21 +859,22 @@ function votingProcess(posts, power_per_vote) { }) } + function sendVote(post, retries, power_per_vote) { - utils.log('Voting on: ' + post.url + ' with count'+post.json.step_count); + utils.log('Voting on: ' + post.url + ' with count'+post.json.step_count); var token_count = post.post_score;//parseFloat(post.rate_multiplier)*100; var vote_weight = Math.floor(post.rate_multiplier * power_per_vote); - post_rank += 1; - utils.log('|#'+post_rank+'|@'+post.author+'|'+ post.json.step_count +'|'+token_count+' Tokens|'+utils.format(vote_weight / 100)+'%|[post](https://www.steemit.com'+post.url+')'); + post_rank += 1; + utils.log('|#'+post_rank+'|@'+post.author+'|'+ post.json.step_count +'|'+token_count+' Tokens|'+utils.format(vote_weight / 100)+'%|[post](https://www.steemit.com'+post.url+')'); - if (vote_weight > config.max_vote_per_post){ + if (vote_weight > config.max_vote_per_post){ vote_weight = config.max_vote_per_post; } - post.vote_weight = vote_weight; - last_votes.push(post); + post.vote_weight = vote_weight; + last_votes.push(post); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { if(config.testing){ //resolve(''); if(config.comment_location && config.comment){ @@ -817,44 +886,44 @@ function sendVote(post, retries, power_per_vote) { .catch(err => { reject(err); }) - }, 3000); + }, 5000); }else{ resolve(''); } }else{ - steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { - if (!err && result) { - utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); + steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { + if (!err && result) { + utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); if(config.comment_location && config.comment){ - setTimeout(function () { + setTimeout(function () { sendComment(post, vote_weight) - .then( res => { - resolve(res) - }) - .catch(err => { - reject(err); - }) - }, 3000); + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, 5000); }else{ - resolve(result); + resolve(result); } - } else { - utils.log(err, result); + }else{ + utils.log(err, result); - // Try again one time on error + // Try again one time on error if (retries < 10) - sendVote(post, retries + 1); - else { - var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' - utils.log(message); - reject(err); - //errorEmail(message, config.report_emails); - } - } - }); + sendVote(post, retries + 1); + else { + var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' + utils.log(message); + reject(err); + //errorEmail(message, config.report_emails); + } + } + }); } - }); + }); } @@ -892,37 +961,48 @@ function sendComment(post, vote_weight) { var rate_multiplier = post.rate_multiplier; var post_step_count = post.json.step_count; - var content = null; - // Return promise - return new Promise((resolve, reject) => { - content = fs.readFileSync(config.comment_location, "utf8"); + var content = null; + // Return promise + return new Promise((resolve, reject) => { + content = fs.readFileSync(config.comment_location, "utf8"); - // If promotion content is specified in the config then use it to comment on the upvoted post - if (content && content != '') { + // If promotion content is specified in the config then use it to comment on the upvoted post + if (content && content != '') { - // Generate the comment permlink via steemit standard convention - var permlink = 're-' + parentAuthor.replace(/\./g, '') + '-' + parentPermlink + '-' + new Date().toISOString().replace(/-|:|\./g, '').toLowerCase(); + // Generate the comment permlink via steemit standard convention + var permlink = 're-' + parentAuthor.replace(/\./g, '') + '-' + parentPermlink + '-' + new Date().toISOString().replace(/-|:|\./g, '').toLowerCase(); var token_count = post.post_score;//parseFloat(rate_multiplier)*100; - - // Replace variables in the promotion content + + // Replace variables in the promotion content content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); - + //replace(/\{milestone\}/g, milestone_txt). - + //adding proper meta content for later relevant reward via afit_tokens data var jsonMetadata = { tags: ['actifit'], app: 'actifit/v'+version, activity_count: post_step_count, user_rank: post.user_rank, content_score: post.content_score, media_score: post.media_score, upvote_score: post.upvote_score, comment_score: post.comment_score, user_rank_score: post.user_rank_score, moderator_score: post.moderator_score, post_activity_score: post.activity_score, afit_tokens: token_count, post_upvote: vote_weight }; + + //if user is lucky winner, add a relevant message + + if (typeof post.lucky_winner != 'undefined' && post.lucky_winner != '' && post.lucky_winner != 'undefined'){ + content = content.replace(/\{lucky_reward}/g,'**You were also selected randomly as a LUCKY WINNER for the day. Your rewards were DOUBLED - DOUBLE CONGRATS!!**'); + jsonMetadata.lucky_winner = 1; + }else{ + content = content.replace(/\{lucky_reward}/g,'') + } + + if (!config.testing){ - // Broadcast the comment - steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { - if (!err && result) { - utils.log('Posted comment: ' + permlink); - resolve(result); - } else { - utils.log('Error posting comment: ' + permlink); - reject(err); - } - }); + // Broadcast the comment + steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { + if (!err && result) { + utils.log('Posted comment: ' + permlink); + resolve(result); + } else { + utils.log('Error posting comment: ' + permlink); + reject(err); + } + }); }else{ console.log('comment'); console.log(content); @@ -930,9 +1010,9 @@ function sendComment(post, vote_weight) { resolve(''); } }else{ - reject('Failed to load content'); + reject('Failed to load content'); } -}); + }); // Check if the bot should resteem this post /* if (config.resteem) resteem(parentAuthor, parentPermlink); */ @@ -1031,38 +1111,38 @@ function sendPayment(to, amount, currency, reason, retries, data) { } function claimRewards() { - if (!config.auto_claim_rewards) - return; - - // Make api call only if you have actual reward - if (parseFloat(account.reward_steem_balance) > 0 || parseFloat(account.reward_sbd_balance) > 0 || parseFloat(account.reward_vesting_balance) > 0) { - steem.broadcast.claimRewardBalance(config.posting_key, config.account, account.reward_steem_balance, account.reward_sbd_balance, account.reward_vesting_balance, function (err, result) { - if (err) { - utils.log(err); - } + if (!config.auto_claim_rewards) + return; + + // Make api call only if you have actual reward + if (parseFloat(account.reward_steem_balance) > 0 || parseFloat(account.reward_sbd_balance) > 0 || parseFloat(account.reward_vesting_balance) > 0) { + steem.broadcast.claimRewardBalance(config.posting_key, config.account, account.reward_steem_balance, account.reward_sbd_balance, account.reward_vesting_balance, function (err, result) { + if (err) { + utils.log(err); + } - if (result) { + if (result) { - var rewards_message = "$$$ ==> Rewards Claim"; - if (parseFloat(account.reward_sbd_balance) > 0) { rewards_message = rewards_message + ' SBD: ' + parseFloat(account.reward_sbd_balance); } - if (parseFloat(account.reward_steem_balance) > 0) { rewards_message = rewards_message + ' STEEM: ' + parseFloat(account.reward_steem_balance); } - if (parseFloat(account.reward_vesting_balance) > 0) { rewards_message = rewards_message + ' VESTS: ' + parseFloat(account.reward_vesting_balance); } + var rewards_message = "$$$ ==> Rewards Claim"; + if (parseFloat(account.reward_sbd_balance) > 0) { rewards_message = rewards_message + ' SBD: ' + parseFloat(account.reward_sbd_balance); } + if (parseFloat(account.reward_steem_balance) > 0) { rewards_message = rewards_message + ' STEEM: ' + parseFloat(account.reward_steem_balance); } + if (parseFloat(account.reward_vesting_balance) > 0) { rewards_message = rewards_message + ' VESTS: ' + parseFloat(account.reward_vesting_balance); } - utils.log(rewards_message); + utils.log(rewards_message); - // If there are liquid post rewards, withdraw them to the specified account - if (parseFloat(account.reward_sbd_balance) > 0 && config.post_rewards_withdrawal_account && config.post_rewards_withdrawal_account != '') { + // If there are liquid post rewards, withdraw them to the specified account + if (parseFloat(account.reward_sbd_balance) > 0 && config.post_rewards_withdrawal_account && config.post_rewards_withdrawal_account != '') { - // Send liquid post rewards to the specified account - steem.broadcast.transfer(config.active_key, config.account, config.post_rewards_withdrawal_account, account.reward_sbd_balance, 'Liquid Post Rewards Withdrawal', function (err, response) { + // Send liquid post rewards to the specified account + steem.broadcast.transfer(config.active_key, config.account, config.post_rewards_withdrawal_account, account.reward_sbd_balance, 'Liquid Post Rewards Withdrawal', function (err, response) { if (err){ - utils.log(err, response); + utils.log(err, response); }else{ - utils.log('$$$ Auto withdrawal - liquid post rewards: ' + account.reward_sbd_balance + ' sent to @' + config.post_rewards_withdrawal_account); - } - }); - } - } - }); - } + utils.log('$$$ Auto withdrawal - liquid post rewards: ' + account.reward_sbd_balance + ' sent to @' + config.post_rewards_withdrawal_account); + } + }); + } + } + }); + } } diff --git a/delegations.js b/delegations.js index ef4fa99..e6d7ee0 100644 --- a/delegations.js +++ b/delegations.js @@ -1,5 +1,4 @@ const dsteem = require('dsteem') -const client = new dsteem.Client('https://api.steemit.com') //const client = new dsteem.Client('https://steemd.privex.io') const _ = require('lodash') const moment = require('moment') @@ -8,6 +7,8 @@ const mail = require('./mail') const config = utils.getConfig() +const client = new dsteem.Client(config.active_node) + const MongoClient = require('mongodb').MongoClient const testRun = false; @@ -35,10 +36,11 @@ var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ runRewards(false);//param steemOnlyReward }); - +//utils.lookupAccountPay(); //param steemOnlyReward runRewards(true); +//runRewards(false); function runRewards(steemOnlyReward){ let mongo_conn = config.mongo_uri @@ -58,15 +60,16 @@ function runRewards(steemOnlyReward){ //return; //run for one day - var days = 1; - startProcess(days, steemOnlyReward); - + var delegation_days = 1; + startProcess(delegation_days, steemOnlyReward); + //grab steem prices and proceed checking for beneficiary payouts to AFIT token reward account (full_pay_benef_account) setInterval(loadSteemPrices,5 * 60 * 1000); - + //claim rewards once per hour setInterval(claimRewards,60 * 60 * 1000); - + + //loadSteemPrices(); } else { utils.log(err, 'delegations') mail.sendPlainMail('Database Error', err, config.report_emails) @@ -136,7 +139,7 @@ async function getBenefactorPosts (account, start) { //console.log(op); let matchingAFIT = 0; console.log(op[1]); - let rewardedSP = parseFloat(vestsToSteemPower(op[1].vesting_payout).toFixed(3)) + let rewardedSP = parseFloat(vestsToSteemPower(op[1].vesting_payout).toFixed(3)) console.log("rewardedSP:"+rewardedSP); //calculate dollar value let steemInUSD = rewardedSP * steemPrice; @@ -146,6 +149,14 @@ async function getBenefactorPosts (account, start) { matchingAFIT = steemInUSD / curAFITPrice.unit_price_usd; console.log("matchingAFIT:"+matchingAFIT); + let rewardedSTEEM = parseFloat(op[1].steem_payout.split(' ')[0]) + + console.log("rewardedSTEEM:"+rewardedSTEEM); + let steemPureInUSD = rewardedSTEEM * steemPrice; + + let steemPureToAFIT = steemPureInUSD / curAFITPrice.unit_price_usd; + matchingAFIT += steemPureToAFIT; + let rewardedSBD = parseFloat(op[1].sbd_payout.split(' ')[0]) console.log("rewardedSBD:"+rewardedSBD); @@ -171,7 +182,8 @@ async function getBenefactorPosts (account, start) { url: op[1].permlink, token_count: matchingAFIT, orig_sbd_amount: rewardedSBD, - orig_steem_amount: rewardedSP, + orig_sp_amount: rewardedSP, + orig_steem_amount: rewardedSTEEM, date: new Date(date) } @@ -222,9 +234,9 @@ function loadSteemPrices() { console.log("Loaded SBD price: " + sbdPrice); - let days = 1; + let afit_swap_days = 1; let start = moment().utc().startOf('date').toDate() - let to = moment(start).subtract(days, 'days').toDate() + let to = moment(start).subtract(afit_swap_days, 'days').toDate() //bring the action getBenefactorPosts(config.full_pay_benef_account, to); @@ -250,7 +262,7 @@ async function startProcess (days, steemOnlyReward) { if (lastTx) end = lastTx.tx_number await updateProperties() if (!testRun){ - await processDelegations(config.account, -1, end) + await processDelegations(config.account, -1, end) } let start = moment().utc().startOf('date').subtract(days, 'days').toDate() let txEnd = moment().utc().startOf('date').toDate() @@ -259,8 +271,8 @@ async function startProcess (days, steemOnlyReward) { await processTokenRewards(start, txEnd, days) //update our user token count post reward if (!testRun){ - updateUserTokens(); - } + updateUserTokens(); + } } var d = new Date(); var dayId = d.getDay(); @@ -334,10 +346,10 @@ async function processTokenRewards (start, end, days) { console.log(reward) //only send out funds if not a test run if (!testRun){ - upsertRewardTransaction(reward) + upsertRewardTransaction(reward) + } } } -} async function processSteemRewards (start) { if (!start) start = moment().utc().startOf('date').toDate() @@ -348,10 +360,13 @@ async function processSteemRewards (start) { //load list of alt accounts to reward them instead of actual delegators let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); + console.log('loading alt accounts'); console.log(altAccounts); Promise.all([getAcumulatedSteemPower(from, to, true), getBenefactorRewards(to, start, -1)]).then(values => { const activeDelegations = values[0].users + console.log('***'); + console.log(values[1]); const steemRewards = values[1].split(' ')[0] const sbdRewards = values[1].split(' ')[1] const totalDelegatedSteem = values[0].totalSteem @@ -385,7 +400,7 @@ async function processSteemRewards (start) { user: reward_user, steem: +(o.totalSteem * rewardPerSteem).toFixed(3), sbd: +(o.totalSteem * rewardPerSBD).toFixed(3) - } + } } @@ -428,6 +443,16 @@ async function processSteemRewards (start) { }) } + + + +function vestsToSteemPower (vests) { + vests = Number(vests.split(' ')[0]) + const steemPower = (totalSteem * (vests / totalVests)) + return steemPower +} + + async function processDelegations (account, start, end) { let delegationTransactions = [] let lastTrans = start @@ -502,7 +527,8 @@ async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { // Look for delegation operations if (op[0] === 'comment_benefactor_reward') { console.log(op[1]); - let newSp = vestsToSteemPower(op[1].vesting_payout) + //SP is the sum of conversting vesting payout to SP, and appending any STEEM payouts + let newSp = vestsToSteemPower(op[1].vesting_payout) + parseFloat(op[1].steem_payout.split(' ')[0]) totalSp = totalSp + newSp let newSBD = op[1].sbd_payout.split(' ')[0] totalSBD += parseFloat(newSBD) @@ -523,31 +549,31 @@ async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { async function getActiveDelegations (start, excludeOn) { start = new Date(start) if (excludeOn){ - return collection.aggregate( - [ - { $match: { 'tx_date': { '$lte': start } } }, - { $sort: { delegator: 1, tx_date: 1 } }, - { - $group: - { - _id: '$delegator', - steem_power: { $last: '$steem_power' }, - vests: { $last: '$vesting_shares' }, - tx_date: { $last: '$tx_date' } - } - }, - { $project: - { - _id: '$_id', - delegator: '$_id', - steem_power: 1, - tx_date: start - } - }, - { $match: { 'steem_power': { '$gt': 0 }, 'delegator': {$nin: config.exclude_rewards} } }, - { $sort: { tx_date: 1 } } - ] - ).toArray() + return collection.aggregate( + [ + { $match: { 'tx_date': { '$lte': start } } }, + { $sort: { delegator: 1, tx_date: 1 } }, + { + $group: + { + _id: '$delegator', + steem_power: { $last: '$steem_power' }, + vests: { $last: '$vesting_shares' }, + tx_date: { $last: '$tx_date' } + } + }, + { $project: + { + _id: '$_id', + delegator: '$_id', + steem_power: 1, + tx_date: start + } + }, + { $match: { 'steem_power': { '$gt': 0 }, 'delegator': {$nin: config.exclude_rewards} } }, + { $sort: { tx_date: 1 } } + ] + ).toArray() }else{ return collection.aggregate( [ @@ -625,9 +651,9 @@ async function getAcumulatedSteemPower (from, to, excludeOn) { if (excludeOn){ console.log('excluding users'); weekTxs = await db.collection('delegation_transactions').find( - {'tx_date': {$gt: from, $lt: to}, - 'delegator': {$nin: config.exclude_rewards}}) - .sort({tx_date: 1}).toArray() + {'tx_date': {$gt: from, $lt: to}, + 'delegator': {$nin: config.exclude_rewards}}) + .sort({tx_date: 1}).toArray() }else{ console.log('no exclude'); weekTxs = await db.collection('delegation_transactions').find( @@ -691,11 +717,6 @@ function upsertRewardTransaction (reward) { ) } -function vestsToSteemPower (vests) { - vests = Number(vests.split(' ')[0]) - const steemPower = (totalSteem * (vests / totalVests)) - return steemPower -} async function updateProperties () { // Set STEEM global properties @@ -771,4 +792,4 @@ async function claimRewards(){ }else{ console.log('no rewards to claim for now'); } -} +} \ No newline at end of file diff --git a/utils.js b/utils.js index 2e9cf8a..2f4006d 100644 --- a/utils.js +++ b/utils.js @@ -3,11 +3,13 @@ const steem = require('steem'); var _ = require('lodash'); const axios = require('axios'); const dsteem = require('dsteem'); - +const moment = require('moment') const client = new dsteem.Client('https://api.steemit.com'); var config; +let th_id = -1; + steem.api.setOptions({ url: 'https://api.steemit.com' }); var STEEMIT_100_PERCENT = 10000; @@ -22,6 +24,9 @@ var HOURS = 60 * 60; var totalVestingFund; var totalVestingShares; var botNames; + let properties + let totalVests + let totalSteem function updateSteemVariables() { steem.api.getRewardFund("post", function (e, t) { @@ -68,8 +73,8 @@ var HOURS = 60 * 60; console.log(currentManaPerc); return currentManaPerc; - } - + } + //implement a get current Resource Credits function for normal operations consumption async function getRC(account_name){ var data={"jsonrpc":"2.0","id":1,"method":"condenser_api.get_account_count","params":{}}; @@ -93,14 +98,52 @@ var HOURS = 60 * 60; const estimated_pct = estimated_mana / estimated_max * 100; const res= {"current_mana": current_mana, "last_update_time": last_update_time, "estimated_mana": estimated_mana, "estimated_max": estimated_max, "estimated_pct": estimated_pct.toFixed(2),"fullin":timeTilFullPower(estimated_pct*100)}; - console.log(res); return res; //}); } + //function handles confirming if payment was received + async function confirmPaymentReceived (req) { + getConfig(); + return new Promise((resolve, reject) => { + th_id = setInterval(async function(){ + console.log('check funds'); + steem.api.getAccountHistory(config.signup_account, -1, 3000, (err, transactions) => { + let tx_id = ''; + let paymentFound = false; + for (let txs of transactions) { + let op = txs[1].op + //check if we received a transfer to our target account + //if we found a transfer operation sent to our target account, with the correct memo and the proper amount, proceed + if (op[0] === 'transfer'){ + let sentAmount = op[1].amount.split(' ')[0]; + if (op[1].to === config.signup_account && op[1].memo === req.query.memo && sentAmount >= (parseFloat(req.query.steem_invest)-0.1)){ + console.log('in'); + console.log(op[1]); + tx_id = txs[1].trx_id; + paymentFound = true; + break; + } + } + } + if (paymentFound){ + //need to look again + console.log('found'); + clearInterval(th_id); + resolve(tx_id); + } + }); + }, 5000); + }); + } + //function handles claiming spots for accounts async function claimDiscountedAccount(){ + console.log('claimDiscountedAccount'); + if (typeof config == 'undefined' || config == null){ + getConfig(); + } const claim_op = [ 'claim_account', { @@ -113,15 +156,147 @@ var HOURS = 60 * 60; const privateKey = dsteem.PrivateKey.fromString( config.active_key ); - await client.broadcast.sendOperations(ops, privateKey).then( - function(result) { - console.log(result); - console.log('>>claimed discounted account spot'); - }, - function(error){ - console.log(error); - } + let result = ''; + try{ + result = await client.broadcast.sendOperations(ops, privateKey); + console.log('success'); + return true; + }catch(err){ + console.log(err); + return false; + } + } + + //function handles creating accounts via discounted claimed spots or normal paid method + async function createAccount (username, password){ + if (typeof config == 'undefined' || config == null){ + getConfig(); + } + //check if account exists + const _account = await client.database.call('get_accounts', [[username]]); + //account not available to register + if (_account.length>0) { + console.log('account already exists'); + console.log(_account); + return false; + } + + console.log('account available'); + + //create keys for new account + const ownerKey = dsteem.PrivateKey.fromLogin(username, password, 'owner'); + const activeKey = dsteem.PrivateKey.fromLogin(username, password, 'active'); + const postingKey = dsteem.PrivateKey.fromLogin(username, password, 'posting'); + let memoKey = dsteem.PrivateKey.fromLogin(username, password, 'memo').createPublic(); + + //create auth values for passing to account creation + const ownerAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[ownerKey.createPublic(), 1]], + }; + const activeAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[activeKey.createPublic(), 1]], + }; + const postingAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[postingKey.createPublic(), 1]], + }; + + //container for required ops + let ops = []; + + + //if we have discounted accounts still available, let's do that, otherwise let's pay for account + let creator = config.account; + + const _creator_account = await client.database.call('get_accounts', [ + [creator], + ]); + console.log('current pending claimed accounts: ' + _creator_account[0].pending_claimed_accounts); + + if (_creator_account[0].pending_claimed_accounts > 0) { + + //the create discounted account operation + const create_op = [ + 'create_claimed_account', + { + creator: creator, + new_account_name: username, + owner: ownerAuth, + active: activeAuth, + posting: postingAuth, + memo_key: memoKey, + json_metadata: '', + extensions: [], + } + ]; + ops.push(create_op); + }else{ + + const create_op = [ + 'account_create', + { + fee: '3.000 STEEM', + creator: creator, + new_account_name: username, + owner: ownerAuth, + active: activeAuth, + posting: postingAuth, + memo_key: memoKey, + json_metadata: '', + extensions: [], + } + ]; + ops.push(create_op); + } + + const privateKey = dsteem.PrivateKey.fromString(config.active_key); + //proceed executing the selected operation(s) + let result = ''; + try{ + result = await client.broadcast.sendOperations(ops, privateKey); + console.log('success'); + return true; + }catch(err){ + console.log(err); + return false; + } + } + + //function handles delegating to a specific account + async function delegateToAccount (delegatee, steemPowerAmount){ + if (typeof config == 'undefined' || config == null){ + getConfig(); + } + const privateKey = dsteem.PrivateKey.fromString( + config.full_pay_ac_key ); + //grab matching amount of Vests to delegate + let matchingVests = await steemPowerToVests(steemPowerAmount); + console.log('matchingVests:'+matchingVests); + const op = [ + 'delegate_vesting_shares', + { + delegator: config.full_pay_benef_account, + delegatee: delegatee, + vesting_shares: matchingVests+' VESTS', + }, + ]; + let result = ''; + try{ + result = await client.broadcast.sendOperations([op], privateKey); + console.log('Included in block:'+ result.block_num); + console.log('returning back'); + return true; + }catch(err){ + console.log(err); + console.log('returning back err'); + return false; + } } function getVoteRShares(voteWeight, account, power) { @@ -295,7 +470,7 @@ function format(n, c, d, t) { if(!benefit) continue; - + //check if user is banned var user_banned = false; @@ -313,7 +488,7 @@ function format(n, c, d, t) { dateSurpassed += 1; continue; } - + results.push(post); } //if we got to old posts and received at least 10 posts, inform calling function that no need to move forward further @@ -354,8 +529,10 @@ function format(n, c, d, t) { */ function calcScore(rules_array, factor, value){ var result; + //console.log("rules_array.length:"+rules_array.length); for (var i=0; i= end) + //console.log(date <= start) + + if (date >= end && date <= start) { + //console.log(txs[0]); + let op = txs[1].op + + //console.log(op[0]); + // Look for beneficiary payments + if (!opsArr.includes(op[0])){ + opsArr.push(op[0]); + } + if (op[0] === 'comment_benefactor_reward') { + //console.log('---------------------------------------'); + //console.log(op); + //console.log(op[1]); + let rewardedSP = parseFloat(vestsToSteemPower(op[1].vesting_payout).toFixed(3)) + totalSp += rewardedSP; + //console.log("rewardedSP:"+rewardedSP); + //calculate dollar value + //let steemInUSD = rewardedSP * steemPrice; + //console.log("steemInUSD:"+steemInUSD); + + let rewardedSTEEM = parseFloat(op[1].steem_payout.split(' ')[0]) + total_STEEM += rewardedSTEEM ; + //console.log("rewardedSTEEM:"+rewardedSTEEM); + + //let steemPureInUSD = rewardedSTEEM * steemPrice; + + + let rewardedSBD = parseFloat(op[1].sbd_payout.split(' ')[0]) + + //console.log("rewardedSBD:"+rewardedSBD); + + totalSBD += rewardedSBD; + + //calculate dollar value + //let sbdInUSD = rewardedSBD * sbdPrice; + + }else if (op[0] === 'producer_reward') { + //console.log('date:'+txs[1].timestamp+'op:'+op[1]); + let rewardedSP = parseFloat(vestsToSteemPower(op[1].vesting_shares.split(' ')[0]).toFixed(3)) + producerSPRewards += rewardedSP; + }else if (op[0] === 'curation_reward') { + //console.log(op[1]); + let rewardedSP = parseFloat(vestsToSteemPower(op[1].reward.split(' ')[0]).toFixed(3)) + curTotalSp += rewardedSP; + + //let rewardedSTEEM = parseFloat(op[1].steem_payout.split(' ')[0]) + //curTotalSTEEM += rewardedSTEEM ; + + + //let rewardedSBD = parseFloat(op[1].sbd_payout.split(' ')[0]) + + //console.log("rewardedSBD:"+rewardedSBD); + + //curTotalSBD += rewardedSBD; + } + } else if (date < end){ + break + } + } + + let lastTx = transactions[transactions.length - 1] + //console.log(lastTx[0]); + let lastDate = moment(lastTx[1].timestamp).format() + // console.log(lastDate) + if (lastDate >= end && (txStart == -1 || txStart > limit)){ + txStart = lastTx[0]; + return getAccountPayTransactions(account, start, end) + } + console.log ('querying complete'); + console.log ('---benefic---'); + console.log ('totalSP:'+totalSp); + console.log ('total_STEEM:'+total_STEEM); + console.log ('totalSBD:'+totalSBD); + console.log ('---curation---'); + console.log ('totalSP:'+curTotalSp); + //console.log ('totalSTEEM:'+curTotalSTEEM); + // console.log ('totalSBD:'+curTotalSBD); + console.log ('---witness---'); + console.log ('producerSPRewards:'+producerSPRewards); + + console.log ('---totals---'); + let comSP = parseFloat(totalSp.toFixed(3))+parseFloat(curTotalSp.toFixed(3))+parseFloat(producerSPRewards.toFixed(3))+parseFloat(total_STEEM.toFixed(3)); + console.log ('totalSTEEM:'+comSP.toFixed(3)); + //console.log ('totalSTEEM:'+totalSTEEM.toFixed(3)); + console.log ('totalSBD:'+totalSBD.toFixed(3)); + console.log ('------------'); + //console.log (opsArr); +} + + +function vestsToSteemPower (vests) { + vests = Number(vests.split(' ')[0]) + const steemPower = (totalSteem * (vests / totalVests)) + return steemPower +} + +//function handles conversting SP to Vests +async function steemPowerToVests (steemPower) { + + if (isNaN(totalSteem) || isNaN(totalVests) ){ + properties = await client.database.getDynamicGlobalProperties() + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) + totalVests = Number(properties.total_vesting_shares.split(' ')[0]) + } + return parseFloat(steemPower * totalVests / totalSteem).toFixed(6); +} + module.exports = { getVotingPower: getVotingPower, @@ -409,5 +776,11 @@ function format(n, c, d, t) { getConfig: getConfig, loadBots: loadBots, checkBeneficiary: checkBeneficiary, - asyncForEach: asyncForEach + asyncForEach: asyncForEach, + generateRandomNumber: generateRandomNumber, + lookupAccountPay: lookupAccountPay, + vestsToSteemPower: vestsToSteemPower, + createAccount: createAccount, + delegateToAccount: delegateToAccount, + confirmPaymentReceived: confirmPaymentReceived, } From d4b7bf5c77438058ae10d76ca4cd7bb822a42e51 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 10 Jan 2019 17:30:07 +0200 Subject: [PATCH 057/193] Support for multi-vote and comment attempts Adding support for multi-vote and comment attempts Parametrizing retries count to config file Parametrizing blockchain waiting time to config file --- curation-bot.js | 49 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 094ed59..6aac60a 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -28,6 +28,12 @@ var reward_sys_version = 'v0.2'; var error_sent = false; +//keep alive +var http = require("http"); +setInterval(function() { + http.get("http://actifitvoter.herokuapp.com"); +}, 600000); // every 10 minutes (600000) + var crypto = require('crypto'); @@ -837,7 +843,7 @@ function votingProcess(posts, power_per_vote) { .then( res => { // If there are more posts, vote on the next one after 5 seconds if (posts.length > 0) { - setTimeout(function () { votingProcess(posts, power_per_vote); }, 5000); + setTimeout(function () { votingProcess(posts, power_per_vote); }, config.voting_posting_delay); } else { post_rank = 0; setTimeout(function () { @@ -851,7 +857,7 @@ function votingProcess(posts, power_per_vote) { //since we're done voting, we need to update all user tokens to reflect new rewards updateUserTokens(); //reportEmail(config.report_emails) - }, 5000); + }, config.voting_posting_delay); } }) .catch(err => { @@ -879,14 +885,14 @@ function sendVote(post, retries, power_per_vote) { //resolve(''); if(config.comment_location && config.comment){ setTimeout(function () { - sendComment(post, vote_weight) + sendComment(post, 0, vote_weight) .then( res => { resolve('') }) .catch(err => { reject(err); }) - }, 5000); + }, config.voting_posting_delay); }else{ resolve(''); } @@ -897,14 +903,14 @@ function sendVote(post, retries, power_per_vote) { if(config.comment_location && config.comment){ setTimeout(function () { - sendComment(post, vote_weight) + sendComment(post, 0, vote_weight) .then( res => { resolve(res) }) .catch(err => { reject(err); }) - }, 5000); + }, config.voting_posting_delay); }else{ resolve(result); } @@ -912,9 +918,18 @@ function sendVote(post, retries, power_per_vote) { utils.log(err, result); // Try again one time on error - if (retries < 10) - sendVote(post, retries + 1); - else { + if (retries < config.max_vote_comment_retries){ + //try to vote again + setTimeout(function () { + sendVote(post, retries + 1, power_per_vote) + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, config.voting_posting_delay); + }else { var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' utils.log(message); reject(err); @@ -955,7 +970,7 @@ async function updateUserTokens() { } -function sendComment(post, vote_weight) { +function sendComment(post, retries, vote_weight) { var parentAuthor = post.author; var parentPermlink = post.permlink; var rate_multiplier = post.rate_multiplier; @@ -1000,7 +1015,19 @@ function sendComment(post, vote_weight) { resolve(result); } else { utils.log('Error posting comment: ' + permlink); - reject(err); + if (retries < config.max_vote_comment_retries){ + utils.log('Try again'); + setTimeout(function () { + sendComment(post, retries + 1, vote_weight) + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, config.voting_posting_delay); + } + //reject(err); } }); }else{ From f0069aa3c43c8b66c6b9a616e77fbe69762e7e57 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 11 Jan 2019 17:15:58 +0200 Subject: [PATCH 058/193] Improvements to display of earnings + weekly avg --- utils.js | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/utils.js b/utils.js index 2f4006d..1f4f376 100644 --- a/utils.js +++ b/utils.js @@ -601,6 +601,8 @@ function resetVals(){ producerSPRewards = 0 } +lookupAccountPay(); + async function lookupAccountPay (){ const ONE_DAY = 1; @@ -608,7 +610,7 @@ async function lookupAccountPay (){ const ONE_MONTH = 30; let start_days = 1; - let lookup_days = ONE_DAY; + let lookup_days = ONE_WEEK; let today = moment().utc().startOf('date').toDate() let start = moment(today).subtract(start_days, 'days').toDate() @@ -617,16 +619,16 @@ async function lookupAccountPay (){ //bring the action console.log('start date:'+start) console.log('************actifit rewards***************') - await getAccountPayTransactions('actifit', start, to); + await getAccountPayTransactions('actifit', start, to, lookup_days); //console.log('append actifit.pay rewards:'+start) //await getAccountPayTransactions('actifit.pay', start, to); txStart = -1; console.log('***********append actifit.funds rewards**********') - await getAccountPayTransactions('actifit.funds', start, to); + await getAccountPayTransactions('actifit.funds', start, to, lookup_days); } -async function getAccountPayTransactions (account, start, end) { +async function getAccountPayTransactions (account, start, end, period) { start = moment(start).format() end = moment(end).format() @@ -716,25 +718,46 @@ async function getAccountPayTransactions (account, start, end) { // console.log(lastDate) if (lastDate >= end && (txStart == -1 || txStart > limit)){ txStart = lastTx[0]; - return getAccountPayTransactions(account, start, end) + return getAccountPayTransactions(account, start, end, period) } console.log ('querying complete'); + console.log ('>>period: '+period + ' days') console.log ('---benefic---'); console.log ('totalSP:'+totalSp); + if (period>0 && totalSp>0){ + console.log ('AVG Daily:'+totalSp/period); + } console.log ('total_STEEM:'+total_STEEM); + if (period>0 && total_STEEM>0){ + console.log ('AVG Daily:'+total_STEEM/period); + } console.log ('totalSBD:'+totalSBD); + if (period>0 && totalSBD>0){ + console.log ('AVG Daily:'+totalSBD/period); + } console.log ('---curation---'); console.log ('totalSP:'+curTotalSp); + if (period>0 && curTotalSp>0){ + console.log ('AVG Daily:'+curTotalSp/period); + } //console.log ('totalSTEEM:'+curTotalSTEEM); // console.log ('totalSBD:'+curTotalSBD); console.log ('---witness---'); console.log ('producerSPRewards:'+producerSPRewards); - + if (period>0 && producerSPRewards>0){ + console.log ('AVG Daily:'+producerSPRewards/period); + } console.log ('---totals---'); let comSP = parseFloat(totalSp.toFixed(3))+parseFloat(curTotalSp.toFixed(3))+parseFloat(producerSPRewards.toFixed(3))+parseFloat(total_STEEM.toFixed(3)); console.log ('totalSTEEM:'+comSP.toFixed(3)); + if (period>0 && comSP>0){ + console.log ('AVG Daily:'+comSP/period); + } //console.log ('totalSTEEM:'+totalSTEEM.toFixed(3)); console.log ('totalSBD:'+totalSBD.toFixed(3)); + if (period>0 && totalSBD>0){ + console.log ('AVG Daily:'+totalSBD/period); + } console.log ('------------'); //console.log (opsArr); } From 3a6302288dcb7a3891d12c9344d8dc9d3dc97198 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 11 Jan 2019 19:03:26 +0200 Subject: [PATCH 059/193] Append additional charity related data Implement fix to support storing additional charity data for better tracking and rank calculation --- curation-bot.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/curation-bot.js b/curation-bot.js index 6aac60a..48b8d89 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -656,6 +656,10 @@ function processVotes(query, subsequent) { note: note, reward_system: reward_sys_version } + //also in case of charity, we need to append the actual user + if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ + post_transaction['giver'] = post.author; + } bulk_transactions.find( { @@ -664,6 +668,30 @@ function processVotes(query, subsequent) { url: post_transaction.url }).upsert().replaceOne(post_transaction); + //the proper transaction without reward + if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ + note = "Charity donation reference post transaction without rewards" + let charity_trans = { + user: post.author, + reward_activity: 'Post', + token_count: 0, + url: post.url, + date: new Date(post.created), + note: note, + charity: post.json.charity, + reward_system: reward_sys_version + } + + //we also need to insert another transaction to capture the actual activity/reward by the user + bulk_transactions.find( + { + user: charity_trans.user, + reward_activity: charity_trans.reward_activity, + url: charity_trans.url + }).upsert().replaceOne(charity_trans); + + } + //reward upvoters //make sure we already have a positive rshares var total_post_upv_shares = parseInt(post.vote_rshares); From 68e643e691a0cac8117626bd07cc071469ca3529 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 14 Jan 2019 17:18:31 +0200 Subject: [PATCH 060/193] Switching all logging to utils --- curation-bot.js | 77 +++++++------------------------------------------ 1 file changed, 11 insertions(+), 66 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 48b8d89..094ed59 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -28,12 +28,6 @@ var reward_sys_version = 'v0.2'; var error_sent = false; -//keep alive -var http = require("http"); -setInterval(function() { - http.get("http://actifitvoter.herokuapp.com"); -}, 600000); // every 10 minutes (600000) - var crypto = require('crypto'); @@ -656,10 +650,6 @@ function processVotes(query, subsequent) { note: note, reward_system: reward_sys_version } - //also in case of charity, we need to append the actual user - if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ - post_transaction['giver'] = post.author; - } bulk_transactions.find( { @@ -668,30 +658,6 @@ function processVotes(query, subsequent) { url: post_transaction.url }).upsert().replaceOne(post_transaction); - //the proper transaction without reward - if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ - note = "Charity donation reference post transaction without rewards" - let charity_trans = { - user: post.author, - reward_activity: 'Post', - token_count: 0, - url: post.url, - date: new Date(post.created), - note: note, - charity: post.json.charity, - reward_system: reward_sys_version - } - - //we also need to insert another transaction to capture the actual activity/reward by the user - bulk_transactions.find( - { - user: charity_trans.user, - reward_activity: charity_trans.reward_activity, - url: charity_trans.url - }).upsert().replaceOne(charity_trans); - - } - //reward upvoters //make sure we already have a positive rshares var total_post_upv_shares = parseInt(post.vote_rshares); @@ -871,7 +837,7 @@ function votingProcess(posts, power_per_vote) { .then( res => { // If there are more posts, vote on the next one after 5 seconds if (posts.length > 0) { - setTimeout(function () { votingProcess(posts, power_per_vote); }, config.voting_posting_delay); + setTimeout(function () { votingProcess(posts, power_per_vote); }, 5000); } else { post_rank = 0; setTimeout(function () { @@ -885,7 +851,7 @@ function votingProcess(posts, power_per_vote) { //since we're done voting, we need to update all user tokens to reflect new rewards updateUserTokens(); //reportEmail(config.report_emails) - }, config.voting_posting_delay); + }, 5000); } }) .catch(err => { @@ -913,14 +879,14 @@ function sendVote(post, retries, power_per_vote) { //resolve(''); if(config.comment_location && config.comment){ setTimeout(function () { - sendComment(post, 0, vote_weight) + sendComment(post, vote_weight) .then( res => { resolve('') }) .catch(err => { reject(err); }) - }, config.voting_posting_delay); + }, 5000); }else{ resolve(''); } @@ -931,14 +897,14 @@ function sendVote(post, retries, power_per_vote) { if(config.comment_location && config.comment){ setTimeout(function () { - sendComment(post, 0, vote_weight) + sendComment(post, vote_weight) .then( res => { resolve(res) }) .catch(err => { reject(err); }) - }, config.voting_posting_delay); + }, 5000); }else{ resolve(result); } @@ -946,18 +912,9 @@ function sendVote(post, retries, power_per_vote) { utils.log(err, result); // Try again one time on error - if (retries < config.max_vote_comment_retries){ - //try to vote again - setTimeout(function () { - sendVote(post, retries + 1, power_per_vote) - .then( res => { - resolve(res) - }) - .catch(err => { - reject(err); - }) - }, config.voting_posting_delay); - }else { + if (retries < 10) + sendVote(post, retries + 1); + else { var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' utils.log(message); reject(err); @@ -998,7 +955,7 @@ async function updateUserTokens() { } -function sendComment(post, retries, vote_weight) { +function sendComment(post, vote_weight) { var parentAuthor = post.author; var parentPermlink = post.permlink; var rate_multiplier = post.rate_multiplier; @@ -1043,19 +1000,7 @@ function sendComment(post, retries, vote_weight) { resolve(result); } else { utils.log('Error posting comment: ' + permlink); - if (retries < config.max_vote_comment_retries){ - utils.log('Try again'); - setTimeout(function () { - sendComment(post, retries + 1, vote_weight) - .then( res => { - resolve(res) - }) - .catch(err => { - reject(err); - }) - }, config.voting_posting_delay); - } - //reject(err); + reject(err); } }); }else{ From 75ce5e1dee3f990d7ca7a90c2710760874c81745 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 14 Jan 2019 17:22:21 +0200 Subject: [PATCH 061/193] Committing proper version with utils changes --- curation-bot.js | 326 ++++++++++++++++++++++++++++-------------------- 1 file changed, 194 insertions(+), 132 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 094ed59..6559186 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -28,6 +28,12 @@ var reward_sys_version = 'v0.2'; var error_sent = false; +//keep alive +var http = require("http"); +setInterval(function() { + http.get("http://actifitvoter.herokuapp.com"); +}, 600000); // every 10 minutes (600000) + var crypto = require('crypto'); @@ -126,7 +132,7 @@ var url = config.mongo_uri; if (config.testing){ url = config.mongo_local; } -console.log('db url:'+url); +utils.log('db url:'+url); var db; var collection; @@ -165,7 +171,7 @@ if (fs.existsSync('members.json')) { // Use connect method to connect to the server MongoClient.connect(url, function(err, client) { if(!err) { - console.log("Connected successfully to server "+url); + utils.log("Connected successfully to server "+url); db = client.db(db_name); @@ -195,14 +201,14 @@ async function startProcess() { if(!botNames) botNames = await utils.loadBots(); if (config.detailed_logging) - console.log('Start process'); + utils.log('Start process'); // Load the settings from the config file each time so we can pick up any changes loadConfig(); // Load the bot account info steem.api.getAccounts([config.account], function (err, result) { if (err || !result) - console.log(err, result); + utils.log(err, result); else { account = result[0]; @@ -214,11 +220,11 @@ async function startProcess() { //deactivating condition of 24 hrs to pass var passedOneDay = true;//today >= oneMoreDay; - //console.log('found banned users'); - //console.log(banned_users); + //utils.log('found banned users'); + //utils.log(banned_users); /*for (var n = 0; n < banned_users.length; n++) { - console.log(banned_users[n].user); + utils.log(banned_users[n].user); //if (post.author == banned_users[n].user){ //utils.log('User '+post.author+' is banned, skipping his post:' + post.url); //user_banned = true; @@ -234,33 +240,33 @@ async function startProcess() { if (config.detailed_logging) utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); - console.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); + utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); // We are at voting power kick start - time to vote! - //console.log(vp >= parseFloat(config.vp_kickstart)/100); + //utils.log(vp >= parseFloat(config.vp_kickstart)/100); if (vp >= parseFloat(config.vp_kickstart)/100 || config.testing) { // Check if there are any rewards to claim before voting if (!config.testing){ claimRewards(); } - console.log('lets vote'); + utils.log('lets vote'); skip = true; - console.log('fetch banned users list'); + utils.log('fetch banned users list'); //grab banned user list before rewarding banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); //grab list of moderators var moderator_api_url = config.api_url+'moderators'; var moderator_info = await axios.get(moderator_api_url); - console.log(moderator_info.data); + utils.log(moderator_info.data); var moderator_array = moderator_info.data; moderator_list = []; for (var mod_it=0;mod_itconfig.account_claim_rc_min){ //if we reached min threshold, claim more spots for discounted accounts utils.claimDiscountedAccount(); } }, function(err) { - console.log("Error fetching RC"); - console.log(err); + utils.log("Error fetching RC"); + utils.log(err); }); } } else if(skip) skip = false; else if (!account) - console.log('Loading account data...'); - else console.log('Voting... or waiting for a day to pass'); + utils.log('Loading account data...'); + else utils.log('Voting... or waiting for a day to pass'); } //var post_scores = []; function processVotes(query, subsequent) { - console.log('processVotes'); + utils.log('processVotes'); steem.api.getDiscussionsByCreated(query, async function (err, result) { if (result && !err) { @@ -305,21 +311,22 @@ function processVotes(query, subsequent) { for(var i = 0; i < result.length; i++) { var post = result[i]; - - //if this is a subsequent call, we need to skip first post - if (subsequent && i==0){ - // console.log('skip post:'+post.title); - //continue to next element - continue; - } + - //if this is the last post, save it to skip it in next iteration - if (i == result.length - 1){ - utils.log('storing last post iteration: ' + post.url); - //update query element to include the most recent post for a starting point of the next iteration - query['start_permlink'] = post.permlink; - query['start_author'] = post.author; - } + //if this is a subsequent call, we need to skip first post + if (subsequent && i==0){ + // utils.log('skip post:'+post.title); + //continue to next element + continue; + } + + //if this is the last post, save it to skip it in next iteration + if (i == result.length - 1){ + utils.log('storing last post iteration: ' + post.url); + //update query element to include the most recent post for a starting point of the next iteration + query['start_permlink'] = post.permlink; + query['start_author'] = post.author; + } // Make sure the post is older than config time if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { @@ -417,12 +424,12 @@ function processVotes(query, subsequent) { //test the result to the post's relevant data if (post.json.actiCrVal != encr_txt){ //wrong, skip post - console.log('post has incorrect actiCrVal'); + utils.log('post has incorrect actiCrVal'); continue; } - //console.log('post is valid'); + //utils.log('post is valid'); }else{ - console.log('post does not contain actiCrVal'); + utils.log('post does not contain actiCrVal'); continue; } @@ -461,14 +468,14 @@ function processVotes(query, subsequent) { if (!actifit_img_urls.includes($(this).attr('src'))){ new_imgs += 1; recorded_imgs.push($(this).attr('src')); - //console.log($(this).attr('src')); + //utils.log($(this).attr('src')); } }); //grab listing of recorded images as part of json var json_img_list = post.json.image; - //console.log(json_img_list); + //utils.log(json_img_list); //try to see if some images were not captured by our approach for HTML content, and grab them from json meta if (json_img_list.length>0){ @@ -481,9 +488,9 @@ function processVotes(query, subsequent) { }; } - //console.log('2>>>new_imgs:'+new_imgs); + //utils.log('2>>>new_imgs:'+new_imgs); - //console.log('>>>> unique images:'+new_imgs); + //utils.log('>>>> unique images:'+new_imgs); //calculate img score post.media_score = utils.calcScore(img_rules, config.media_factor, new_imgs); @@ -491,7 +498,7 @@ function processVotes(query, subsequent) { //calculate upvote score relying on positive votes only post.upvote_score = utils.calcScore(upv_rules, config.upvotes_factor, post.net_votes); - //console.log('upvotes:'+post.net_votes); + //utils.log('upvotes:'+post.net_votes); /***************** moderator upvote factor ******************/ @@ -500,49 +507,50 @@ function processVotes(query, subsequent) { post.active_votes.some(function(vote){ if (moderator_list.includes(vote.voter)){ post.moderator_score = parseInt(config.moderator_upvote_factor); - //console.log('found moderator upvote'+vote.voter); + //utils.log('found moderator upvote'+vote.voter); return true; } }); - //console.log(post.moderator_score); + //utils.log(post.moderator_score); /******************* comments criteria *********************/ - - - let comments = await steem.api.getContentRepliesAsync(post.author, post.permlink); var matching_comment_count = 0; - for(var cmt_it = 0; cmt_it < comments.length; cmt_it++) { - //console.log('>>>>>>'+comments[cmt_it].body); - const $ = cheerio.load('
'+comments[cmt_it].body+'
'); - var comment_pure = $('.comment_container').text().replace(/\s+/g,' '); - //console.log(comment_pure); - if (comment_pure.length > 50){ - matching_comment_count += 1; - } + //if (!config.testing){ + let comments = await steem.api.getContentRepliesAsync(post.author, post.permlink); - //check if the comment is made by a moderator, if it is we need to reward the moderator - if (moderator_list.includes(comments[cmt_it].author)){ - let comment_transaction = { - user: comments[cmt_it].author, - reward_activity: 'Moderator Comment', - token_count: parseInt(config.moderator_comment_reward), - url: post.url, - comment_url: comments[cmt_it].url, - date: new Date(comments[cmt_it].created) + for(var cmt_it = 0; cmt_it < comments.length; cmt_it++) { + //utils.log('>>>>>>'+comments[cmt_it].body); + const $ = cheerio.load('
'+comments[cmt_it].body+'
'); + var comment_pure = $('.comment_container').text().replace(/\s+/g,' '); + //utils.log(comment_pure); + if (comment_pure.length > 50){ + matching_comment_count += 1; + } + + //check if the comment is made by a moderator, if it is we need to reward the moderator + if (moderator_list.includes(comments[cmt_it].author)){ + let comment_transaction = { + user: comments[cmt_it].author, + reward_activity: 'Moderator Comment', + token_count: parseInt(config.moderator_comment_reward), + url: post.url, + comment_url: comments[cmt_it].url, + date: new Date(comments[cmt_it].created) + } + bulk_transactions.find( + { + user: comment_transaction.user, + reward_activity: comment_transaction.reward_activity, + url: comment_transaction.url, + comment_url: comment_transaction.comment_url + }).upsert().replaceOne(comment_transaction); + utils.log('found comment>>>>'); + utils.log(comment_transaction); } - bulk_transactions.find( - { - user: comment_transaction.user, - reward_activity: comment_transaction.reward_activity, - url: comment_transaction.url, - comment_url: comment_transaction.comment_url - }).upsert().replaceOne(comment_transaction); - console.log('found comment>>>>'); - console.log(comment_transaction); } - } - //console.log("comments:"+matching_comment_count); + //} + //utils.log("comments:"+matching_comment_count); //calculate comment score post.comment_score = utils.calcScore(cmts_rules, config.comments_factor, matching_comment_count); @@ -550,11 +558,11 @@ function processVotes(query, subsequent) { //var request = require('request'); var rank_api_url = config.api_url+'getRank/'+post.author; var user_rank_info = await axios.get(rank_api_url); - //console.log(user_rank_info.user_rank); + //utils.log(user_rank_info.user_rank); post.user_rank = user_rank_info.data.user_rank; //calculate user rank score relying on positive votes only post.user_rank_score = parseFloat(user_rank_info.data.user_rank)*parseInt(config.rank_factor)/100; - //console.log('rank'+post.user_rank_score); + //utils.log('rank'+post.user_rank_score); //calculate total post score @@ -563,11 +571,11 @@ function processVotes(query, subsequent) { //rate multiplier to allow assigning proper steem upvote value per each post according to its post_score/afit payout post.rate_multiplier = post.post_score / 100; //post_scores.push([post.url,post.post_score]); - //console.log(post); + //utils.log(post); } catch (err) { - console.log('Error parsing json metadata'); - console.log(err); + utils.log('Error parsing json metadata'); + utils.log(err); continue; } @@ -579,7 +587,7 @@ function processVotes(query, subsequent) { let first_index = _.findIndex(votePosts, ['author', post.author]); if (last_index != -1 && (first_index!=last_index)) { - console.log('---- User already has more than 2 posts in 24 hours ------'); + utils.log('---- User already has more than 2 posts in 24 hours ------'); let last_voted = votePosts[last_index]; var last_date = moment(last_voted.created).format('D'); let first_voted = votePosts[first_index]; @@ -587,42 +595,42 @@ function processVotes(query, subsequent) { var this_date = moment(post.created).format('D'); //if all 3 dates match, skip it if ((last_date == this_date) && (first_date == this_date)) { - console.log('---- Last voted -----'); - console.log(new Date (last_voted.created)); - console.log('---- First voted -----'); - console.log(new Date (first_voted.created)); - console.log('---- This voted -----'); - console.log(new Date (post.created)); - console.log('---- Moment-----'); - console.log(last_date); - console.log(first_date); - console.log(this_date); + utils.log('---- Last voted -----'); + utils.log(new Date (last_voted.created)); + utils.log('---- First voted -----'); + utils.log(new Date (first_voted.created)); + utils.log('---- This voted -----'); + utils.log(new Date (post.created)); + utils.log('---- Moment-----'); + utils.log(last_date); + utils.log(first_date); + utils.log(this_date); continue; } }else if (last_index != -1){ - console.log('last_index:'+last_index); - console.log(post.author+post.url); + utils.log('last_index:'+last_index); + utils.log(post.author+post.url); //adding condition to reject a post if a prior one exists that is less than 6 hours away let last_voted = votePosts[last_index]; - //console.log(last_voted.author+last_voted.url); + //utils.log(last_voted.author+last_voted.url); var last_date = moment(last_voted.created).toDate(); var this_date = moment(post.created).toDate(); //check the hours difference var hours_diff = Math.abs(this_date - last_date) / 36e5; if (hours_diff0){ //calculate max token payment based upon post pending payout var max_afits = Math.min(parseFloat(post.pending_payout_value) * parseFloat(config.per_post_alloc_afits), parseFloat(config.per_post_alloc_afits)); - //console.log('max afits '+max_afits); + utils.log('max afits '+max_afits); + utils.log(post.active_votes); post.active_votes.forEach(async vote => { //grab user's contribution to the upvote pool @@ -691,13 +731,13 @@ function processVotes(query, subsequent) { }).upsert().replaceOne(vote_transaction); //transactions.push(vote_transaction); - //console.log(vote_transaction); + //utils.log(vote_transaction); } }); } //result = posts_collection.insert(post); }catch(err){ - console.log(err); + utils.log(err); } }//end of loop going through posts @@ -706,13 +746,13 @@ function processVotes(query, subsequent) { //store posts await bulk.execute(); }catch(bulkerr){ - console.log(bulkerr); + utils.log(bulkerr); } try{ //award transaction tokens bulk_transactions.execute(); }catch(bulkerr){ - console.log(bulkerr); + utils.log(bulkerr); } } @@ -723,8 +763,8 @@ function processVotes(query, subsequent) { //update last count lastIterationCount = votePosts.length; //call again with subsequent enabled to avoid duplicate posts, disparse the calls by 1 sec to avoid API timeouts - console.log("query:"+query['tag']); - console.log("query:"+query['start_permlink']); + utils.log("query:"+query['tag']); + utils.log("query:"+query['start_permlink']); setTimeout(processVotes, 1000, query, true); @@ -749,8 +789,8 @@ function processVotes(query, subsequent) { lucky_winner_id = utils.generateRandomNumber(1, votePosts.length); let post = votePosts[lucky_winner_id]; - console.log('before'); - console.log(votePosts[lucky_winner_id].post_score); + utils.log('before'); + utils.log(votePosts[lucky_winner_id].post_score); let reward_user = post.author; let activity_type = 'Post'; @@ -796,10 +836,10 @@ function processVotes(query, subsequent) { //award transaction tokens await bulk_transactions.execute(); }catch(bulkerr){ - console.log(bulkerr); + utils.log(bulkerr); } - console.log('after'); - console.log(votePosts[lucky_winner_id].post_score); + utils.log('after'); + utils.log(votePosts[lucky_winner_id].post_score); /********************* proceed with STEEM upvotes ************************/ @@ -807,13 +847,14 @@ function processVotes(query, subsequent) { var tot_weight = 0; for (var xx=0;xx { // If there are more posts, vote on the next one after 5 seconds if (posts.length > 0) { - setTimeout(function () { votingProcess(posts, power_per_vote); }, 5000); + setTimeout(function () { votingProcess(posts, power_per_vote); }, config.voting_posting_delay); } else { post_rank = 0; setTimeout(function () { @@ -851,11 +892,11 @@ function votingProcess(posts, power_per_vote) { //since we're done voting, we need to update all user tokens to reflect new rewards updateUserTokens(); //reportEmail(config.report_emails) - }, 5000); + }, config.voting_posting_delay); } }) .catch(err => { - console.log(err); + utils.log(err); }) } @@ -879,14 +920,14 @@ function sendVote(post, retries, power_per_vote) { //resolve(''); if(config.comment_location && config.comment){ setTimeout(function () { - sendComment(post, vote_weight) + sendComment(post, 0, vote_weight) .then( res => { resolve('') }) .catch(err => { reject(err); }) - }, 5000); + }, config.voting_posting_delay); }else{ resolve(''); } @@ -897,14 +938,14 @@ function sendVote(post, retries, power_per_vote) { if(config.comment_location && config.comment){ setTimeout(function () { - sendComment(post, vote_weight) + sendComment(post, 0, vote_weight) .then( res => { resolve(res) }) .catch(err => { reject(err); }) - }, 5000); + }, config.voting_posting_delay); }else{ resolve(result); } @@ -912,9 +953,18 @@ function sendVote(post, retries, power_per_vote) { utils.log(err, result); // Try again one time on error - if (retries < 10) - sendVote(post, retries + 1); - else { + if (retries < config.max_vote_comment_retries){ + //try to vote again + setTimeout(function () { + sendVote(post, retries + 1, power_per_vote) + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, config.voting_posting_delay); + }else { var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' utils.log(message); reject(err); @@ -929,7 +979,7 @@ function sendVote(post, retries, power_per_vote) { //function handles updating current user token count async function updateUserTokens() { - console.log('---- Updating Users ----'); + utils.log('---- Updating Users ----'); try{ //group all token transactions per user, and sum them to generate new total count @@ -950,12 +1000,12 @@ async function updateUserTokens() { //insert new count per user await db.collection('user_tokens').insert(user_tokens); }catch(err){ - console.log('>>save data error:'+err.message); + utils.log('>>save data error:'+err.message); } } -function sendComment(post, vote_weight) { +function sendComment(post, retries, vote_weight) { var parentAuthor = post.author; var parentPermlink = post.permlink; var rate_multiplier = post.rate_multiplier; @@ -1000,13 +1050,25 @@ function sendComment(post, vote_weight) { resolve(result); } else { utils.log('Error posting comment: ' + permlink); - reject(err); + if (retries < config.max_vote_comment_retries){ + utils.log('Try again'); + setTimeout(function () { + sendComment(post, retries + 1, vote_weight) + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, config.voting_posting_delay); + } + //reject(err); } }); }else{ - console.log('comment'); - console.log(content); - console.log(jsonMetadata); + utils.log('comment'); + utils.log(content); + utils.log(jsonMetadata); resolve(''); } }else{ @@ -1035,9 +1097,9 @@ function errorEmail(message, to) { mail.sendPlainMail('Info Mail', message, to) .then(function(res, err) { if (!err) { - console.log(res); + utils.log(res); } else { - console.log(err); + utils.log(err); } }); } From 0c827da5ee4f0d97f2a5697f35fcd6d2821a70ca Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Tue, 15 Jan 2019 11:07:43 +0200 Subject: [PATCH 062/193] Switch from vote_rshares to net_rshares When fetching discussions, total shares, and relevant votes, we needed to switch from using post.vote_rshares (as it was deprecated) to using post.net_rshares which works properly and contains the actual net value of votes/shares --- curation-bot.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 6559186..2e49147 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -696,16 +696,16 @@ function processVotes(query, subsequent) { //reward upvoters //make sure we already have a positive rshares - utils.log('post.vote_rshares'); - utils.log(post.vote_rshares); - var total_post_upv_shares = parseInt(post.vote_rshares); - utils.log('total_post_upv_shares'+total_post_upv_shares); + //switching to net_rshares as the older vote_rshares is deprecated + var total_post_upv_shares = parseInt(post.net_rshares); + //utils.log('total_post_upv_shares'+total_post_upv_shares); if (total_post_upv_shares>0){ //calculate max token payment based upon post pending payout var max_afits = Math.min(parseFloat(post.pending_payout_value) * parseFloat(config.per_post_alloc_afits), parseFloat(config.per_post_alloc_afits)); utils.log('max afits '+max_afits); - utils.log(post.active_votes); + + //utils.log(post.active_votes); post.active_votes.forEach(async vote => { //grab user's contribution to the upvote pool @@ -715,6 +715,7 @@ function processVotes(query, subsequent) { if (post.author != vote.voter && upv_tokens>0){ //calculate the percentage of the user's contribution, and allocate him his AFIT tokens share var voter_tokens = upv_tokens / total_post_upv_shares * max_afits; + //console.log(voter_tokens); voter_tokens = parseFloat(voter_tokens.toFixed(3)); let vote_transaction = { user: vote.voter, From f05ca56d7b364607a75a829ced1bc67933d0792f Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 18 Jan 2019 01:14:20 +0200 Subject: [PATCH 063/193] Implement new security endpoints Implement new endpoints for storing and retrieving verified posts --- app.js | 2081 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 1062 insertions(+), 1019 deletions(-) diff --git a/app.js b/app.js index cdf5e06..51810e7 100644 --- a/app.js +++ b/app.js @@ -1,1019 +1,1062 @@ -var express = require('express'); -var exphbs = require('express-handlebars'); -const MongoClient = require('mongodb').MongoClient; -var utils = require('./utils'); -const moment = require('moment') - -var appPort = process.env.PORT || 3120; - -var app = express(); - -app.engine('handlebars', exphbs({defaultLayout: 'main'})); -app.set('view engine', 'handlebars'); - -var config = utils.getConfig(); - -// Connection URL -let url = config.mongo_uri; -if (config.testing){ - url = config.mongo_local; -} - -var db; -var collection; -// Database Name -const db_name = config.db_name; -const collection_name = 'user_tokens'; - -// Use connect method to connect to the server -MongoClient.connect(url, function(err, client) { - if(!err) { - console.log("Connected successfully to server"); - - db = client.db(db_name); - - // Get the documents collection - collection = db.collection(collection_name); - } else { - utils.log(err, 'api'); - } - -}); - -//allows setting acceptable origins to be included across all function calls -app.use(function(req, res, next) { - var allowedOrigins = ['*', 'https://actifit.io']; - var origin = req.headers.origin; - if(allowedOrigins.indexOf(origin) > -1){ - res.setHeader('Access-Control-Allow-Origin', origin); - } - return next(); -}); - -app.get('/', function (req, res) { - var data = {}; - data.posts = [ - { - url: 'dsadsa', - net_votes: 44, - vote_weight: "0.03" - }]; - data.total_votes = 323; - data.total_money = "$0.63"; - // res.render('home', data); - res.send('Hello there!'); -}); - - -/* function handles calculating and returning user token count */ -grabUserTokensFunc = async function (req, res){ - let user = await collection.findOne({_id: req.params.user}, {fields : { _id:0} }); - console.log(user); - //fixing token amount display for 3 digits - if (typeof user!= "undefined" && user!=null){ - if (typeof user.tokens!= "undefined"){ - user.tokens = user.tokens.toFixed(3) - } - } - return user; -} - -/* end point for user total token count display */ -app.get('/user/:user', async function (req, res) { - let user = await grabUserTokensFunc(req,res); - res.send(user); -}); - -/* end point for user transactions display (per user or general actifit token transactions, limited by 1000 */ -app.get('/transactions/:user?', async function (req, res) { - let query = {}; - var transactions; - if(req.params.user){ - query = {user: req.params.user} - transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); - }else{ - //only limit returned transactions in case this is a general query - transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); - } - res.send(transactions); -}); - -/* end point for user referrals display (per user or general referrals */ -app.get('/signups/:user?', async function (req, res) { - let query = {account_created: true}; - var referrals; - if(req.params.user){ - query['referrer'] = req.params.user; - referrals = await db.collection('signup_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); - }else{ - //only limit returned referrals in case this is a general query - referrals = await db.collection('signup_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); - } - res.send(referrals); -}); - -/* end point for returning number of awarded users and tokens distributed */ -app.get('/user-tokens-info', async function(req, res) { - - await db.collection(collection_name).aggregate([ - { - $match: {} - }, - { - $group: - { - _id: null, - tokens_distributed: { $sum: "$tokens" }, - user_count: { $sum: 1 } - } - } - ]).toArray(function(err, results) { - var output = 'rewarded users:'+results[0].user_count+','; - output += 'tokens distributed:'+results[0].tokens_distributed; - res.send(results); - console.log(results); - }); - -}); - - -/* end point for returning total delegation payments (number of delegators and amount paid) on a specific date */ -app.get('/delegationPayments', async function(req, res) { - var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - var dateRegex = todayDate; // /^2018-08-05/ - if (req.query.targetDate){ - dateRegex = req.query.targetDate; - } - - await db.collection('token_transactions').aggregate([ - { - $match: - { - "reward_activity": "Delegation", - "date": { - '$eq' : new Date(dateRegex) - } - } - }, - { - $group: - { - _id: null, - tokens_distributed: { $sum: "$token_count" }, - user_count: { $sum: 1 } - } - } - ]).toArray(function(err, results) { - res.send(results); - console.log(results); - }); - -}); - - -/* end point for returning total payments (categorized by reward type as well as a full total) on a specific date */ -app.get('/totalTokensDistributed', async function(req, res) { - - var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - if (req.query.targetDate){ - startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); - } - var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); - console.log("startDate:"+startDate+" endDate:"+endDate); - - await db.collection('token_transactions').aggregate([ - { - $match: - { - "date": { - "$lte": new Date(endDate), - "$gt": new Date(startDate) - } - } - }, - { - $group: - { - _id: {reward_activity:"$reward_activity"}, - tokens_distributed: { $sum: "$token_count" }, - } - } - ]).toArray(function(err, results) { - //also append total token count to the grouped display - let tot_tokens = 0; - for (let entry of results) { - tot_tokens += entry.tokens_distributed; - } - console.log(tot_tokens); - results.push([{"_id":null,"tokens_distributed":tot_tokens}]); - - res.send(results); - console.log(results); - }); - -}); - -/* end point for returning count of posts/activities rewarded */ -app.get('/rewarded-activity-count', async function(req, res) { - - await db.collection("posts").aggregate( [ - { $count: "reward_count" } - ]).toArray(function(err, results) { - console.log(results); - utils.log(results, 'rewarded-activity-count'); - res.send(results); - }); -}); - -/* end point for returning charity data supported by actifit */ -app.get('/charities', async function (req, res) { - var charities = await db.collection('available_charities').find({status:"enabled"}, {charity_name: 1}).sort({charity_name: 1}).toArray(); - res.send(charities); -}); - -/* end point for returning current active delegator data by actifit */ -app.get('/topDelegators', async function (req, res) { - var delegatorList; - if (isNaN(req.query.count)){ - delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).toArray(); - }else{ - delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).limit(parseInt(req.query.count)).toArray(); - } - res.send(delegatorList); -}); - -activeDelegationFunc = async function (userName){ - let user = await db.collection('active_delegations').findOne({_id: userName}, {fields : { _id:0} }); - console.log(user); - return user; -} - -/* end point for returning a single user last recorded active delegation amount */ -app.get('/delegation/:user', async function (req, res) { - var user = await activeDelegationFunc(req.params.user); - res.send(user); -}); - -moderatorsListFunc = async function () { - let moderatorList = await db.collection('team').find({title:'moderator', status:'active'}).sort({name: 1}).toArray(); - return moderatorList; -} - -/* end point for returning current active moderators data by actifit */ -app.get('/moderators', async function (req, res) { - var moderatorList; - moderatorList = await moderatorsListFunc(); - res.send(moderatorList); -}); - -/* end point for returning current active ambassadors data by actifit */ -app.get('/ambassadors', async function (req, res) { - var ambassadorList; - ambassadorList = await db.collection('team').find({title:'ambassador', status:'active'}).sort({name: 1}).toArray(); - res.send(ambassadorList); -}); - -/* end point for returning current top AFIT token holders */ -app.get('/topTokenHolders', async function (req, res) { - var tokenHolders; - if (isNaN(req.query.count)){ - tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).toArray(); - }else{ - tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); - } - let output = tokenHolders; - if (req.query.pretty){ - output = '#|Token Holder | AFIT Tokens Held |
'; - output += '|---|---|---|
'; - for(var i = 0; i < tokenHolders.length; i++) { - let tokenHolder = tokenHolders[i]; - output += (i+1) + '|'; - output += '@'+tokenHolder.user + '|'; - output += gk_add_commas(tokenHolder.tokens.toFixed(3)) + '|'; - output += '
'; - } - } - res.send(output); -}); - - -/* end point for returning accounts banned by actifit*/ -app.get('/banned_users', async function (req, res) { - var banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); - res.send(banned_users); -}); - - -/* end point for counting number of reblogs on a certain date param (default current date) */ -app.get('/reblogCount', async function (req, res) { - var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - //fileName = "steemrewards"+fileName+".json"; - var dateRegex = new RegExp ('^'+todayDate); // /^2018-08-05/ - if (req.query.targetDate){ - dateRegex = new RegExp ('^'+req.query.targetDate); - } - let query = await db.collection('token_transactions').find({ - "reward_activity": "Post Reblog", - "date": dateRegex - }) - try{ - console.log('counting'); - let reblog_count = await query.count(); - console.log(reblog_count); - res.send(JSON.stringify({reblog_count:reblog_count})); - }catch(err){ - console.log(err.message); - } -}); - -/* end point for counting number of upvotes on a certain date param (default current date) */ -app.get('/upvoteCount', async function (req, res) { - - var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - if (req.query.targetDate){ - startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); - } - var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); - console.log("startDate:"+startDate+" endDate:"+endDate); - //adjust query to include dates - query_json = { - "reward_activity": "Post Vote", - "date": { - "$lte": new Date(endDate), - "$gt": new Date(startDate) - } - }; - - let query = await db.collection('token_transactions').find(query_json); - - try{ - console.log('counting'); - let upvote_count = await query.count(); - console.log(upvote_count); - res.send(JSON.stringify({upvote_count:upvote_count})); - }catch(err){ - console.log(err.message); - } -}); - - -/* end point for counting number of rewarded posts on a certain date param (default current date) */ -app.get('/rewardedPostCount', async function (req, res) { - - var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - if (req.query.targetDate){ - startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); - } - var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); - console.log("startDate:"+startDate+" endDate:"+endDate); - //adjust query to include dates - query_json = { - "reward_activity": "Post", - "date": { - "$lte": new Date(endDate), - "$gt": new Date(startDate) - } - }; - - let query = await db.collection('token_transactions').find(query_json); - - try{ - console.log('counting'); - let rewarded_post_count = await query.count(); - console.log(rewarded_post_count); - res.send(JSON.stringify({rewarded_post_count:rewarded_post_count})); - }catch(err){ - console.log(err.message); - } -}); - -/* refactored function to grab rewarded post count per user for use across get calls */ -userRewardedPostCountFunc = async function(req, res){ - var user = req.params.user; - //default query - var query_json = { - "reward_activity": "Post", - "user": user - }; - //if this is a sum for specific period v/s a total sum - if (typeof req.query.period != "undefined" && !isNaN(req.query.period)){ - var days = req.query.period; - //console.log("days:"+days); - var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - var endDate = moment(moment().utc().startOf('date').subtract(days, 'days').toDate()).format('YYYY-MM-DD'); - //console.log("startDate:"+startDate+" endDate:"+endDate); - //adjust query to include dates - query_json = { - "reward_activity": "Post", - "user": user, - "date": { - "$gte": new Date(endDate), - "$lt": new Date(startDate) - } - }; - } - - //build up query accordingly - let query = await db.collection('token_transactions').find(query_json); - try{ - //grab total number of matching records - let rewarded_post_count = await query.count(); - //console.log("rewarded_post_count:"+rewarded_post_count); - - return rewarded_post_count; - }catch(err){ - console.log(err.message); - return ""; - } -} - -/* end point for counting number of rewarded posts on a certain date param (default current date) */ -app.get('/userRewardedPostCount/:user', async function (req, res) { - - //grab user account - if (typeof req.params.user!= "undefined" && req.params.user!=null){ - - var rewarded_post_count = await userRewardedPostCountFunc(req, res); - res.send(JSON.stringify({rewarded_post_count:rewarded_post_count})); - }else{ - res.send(""); - } -}); - -/* end point for getting current user's Actifit rank */ -app.get('/getRank/:user', async function (req, res) { - - if (typeof req.params.user!= "undefined" && req.params.user!=null){ - - //delegation calculation matrix - var delegation_rules = [ - [9,0], - [499,0.05], - [999,0.10], - [4999,0.20], - [9999,0.30], - [19999,0.40], - [49999,0.55], - [99999,0.65], - [499999,0.75], - [999999,0.90], - [1000000,1] - ] - - //AFIT token calculation matrix - var afit_token_rules = [ - [9,0], - [999,0.10], - [4999,0.20], - [9999,0.30], - [19999,0.40], - [49999,0.50], - [99999,0.60], - [499999,0.70], - [999999,0.80], - [4999999,0.90], - [5000000,1] - ] - - //Rewarded Posts calculation matrix - var rewarded_posts_rules = [ - [9,0], - [29,0.10], - [59,0.20], - [89,0.30], - [119,0.40], - [179,0.50], - [359,0.60], - [539,0.70], - [719,0.80], - [1079,0.90], - [1080,1] - ] - - //Rewarded Posts calculation matrix - var recent_reward_posts_rules = [ - [0,0], - [2,0.20], - [4,0.40], - [6,0.60], - [8,0.80], - [9,1] - ] - - var user_rank = 0; - - //grab delegation amount - var userDelegations = await activeDelegationFunc(req.params.user); - - let delegSP = 0; - //get current delegated SP if any - if (userDelegations != null){ - console.log('already delegated'); - delegSP = userDelegations.steem_power; - } - //console.log(userDelegations.steem_power); - - var delegation_score = 0; - - //check if the user has an alt account as beneficiary - let delegator_info = await getAltAccountStatusFunc(req.params.user); - //check if returned object is not empty - if (Object.keys(delegator_info).length > 0){ - if (parseInt(delegator_info.user_rank_benefit) == 1){ - //consider as no delegations - delegSP = 0; - } - }else{ - //also check the other case where the account is an alt-account - delegator_info = await getAltAccountByNameFunc(req.params.user); - //check if returned object is not empty - if (Object.keys(delegator_info).length > 0){ - if (parseInt(delegator_info.user_rank_benefit) == 1){ - //get original user delegation amount - userDelegations = await activeDelegationFunc(delegator_info.delegator); - if (userDelegations != null){ - delegSP = userDelegations.steem_power; - } - } - } - } - - - if (parseFloat(delegSP) > 0){ - delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(delegSP)); - } - - user_rank += delegation_score; - - //grab user token count - var userTokens = await grabUserTokensFunc(req,res); - //console.log(userTokens.tokens); - - var afit_tokens_score = 0; - if (userTokens != null){ - afit_tokens_score = utils.calcScore(afit_token_rules, config.afit_token_factor, parseFloat(userTokens.tokens)); - } - - user_rank += afit_tokens_score; - - //grab total rewarded posts count - var tot_rewarded_post_count = await userRewardedPostCountFunc(req, res); - //console.log(tot_rewarded_post_count); - - var tot_posts_score = utils.calcScore(rewarded_posts_rules, config.rewarded_posts_factor, parseInt(tot_rewarded_post_count)); - - user_rank += tot_posts_score; - - //set the check period for config value of days days, and rerun the call to get last rewarded posting activity during this period - req.query.period = config.recent_posts_period; - - var recent_rewarded_post_count = await userRewardedPostCountFunc(req, res); - //console.log(recent_rewarded_post_count); - - var recent_posts_score = utils.calcScore(recent_reward_posts_rules, config.recent_posts_factor, parseInt(recent_rewarded_post_count)); - - user_rank += recent_posts_score; - - var score_components = JSON.stringify({ - user_rank: user_rank, - delegation_score: delegation_score, - afit_tokens_score: afit_tokens_score, - tot_posts_score: tot_posts_score, - recent_posts_score:recent_posts_score - }); - console.log(score_components) - - res.send(score_components); - }else{ - res.send(""); - } -}); - -/* function handles the backbone for grabbing Alt Account Status */ -getAltAccountStatusFunc = async function (user){ - let delegator_info = null; - if (typeof user!= "undefined" && user!=null){ - //in this case, we check the status of a single user - var query_json = { - "delegator": user - }; - - delegator_info = await db.collection('delegation_alt_beneficiaries').findOne(query_json, {fields : { _id:0} }); - if (delegator_info==null){ - delegator_info = {}; - } - console.log(delegator_info); - }else{ - //alternatively grab all alt-account reward delegations - delegator_info = await db.collection('delegation_alt_beneficiaries').find().toArray(); - console.log(delegator_info); - } - return delegator_info; -} - -/* function handles checking if alt-account is linked to a delegator */ -getAltAccountByNameFunc = async function (targetUser){ - let delegator_info = null; - if (typeof targetUser!= "undefined" && targetUser!=null){ - //in this case, we check the status of a single user - var query_json = { - "alt_account": targetUser - }; - - delegator_info = await db.collection('delegation_alt_beneficiaries').findOne(query_json, {fields : { _id:0} }); - if (delegator_info==null){ - delegator_info = {}; - } - console.log(delegator_info); - } - return delegator_info; -} - -/* end point for getting list of SP delegator accounts who wish to move their user rank and/or rewards to their alt-accounts*/ -app.get('/getAltAccountStatus/:user?', async function (req, res) { - let delegator_info = await getAltAccountStatusFunc(req.params.user); - res.send(delegator_info); -}); - -/* function handles processing requests for getting AFIT token pay depending on reward activity type */ -getPostRewardFunc = async function(user, url, reward_activity){ - var query_json = { - "reward_activity": reward_activity, - "user": user, - "url":url - }; - - let post_details = await db.collection('token_transactions').findOne(query_json, {fields : { _id:0} }); - console.log(post_details); - //fixing token amount display for 3 digits - if (typeof post_details!= "undefined" && post_details!=null){ - if (typeof post_details.token_count!= "undefined"){ - return post_details.token_count; - } - } - //otherwise return no tokens - return 0; -} - -/* end point for getting a post's reward */ -app.get('/getPostReward', async function (req, res) { - - if (typeof req.query.user!= "undefined" && req.query.user!=null - && typeof req.query.url!= "undefined" && req.query.url!=null){ - var user = req.query.user; - var url = req.query.url; - console.log('url:'+url); - //grab specific reward type for user and post - var token_count = await getPostRewardFunc(user, url, "Post"); - res.send({token_count: token_count}); - }else{ - res.send({token_count: 0}); - } -}); - -/* end point for retrieving a post's full AFIT Pay reward */ -app.get('/getPostFullAFITPayReward', async function (req, res) { - - if (typeof req.query.user!= "undefined" && req.query.user!=null - && typeof req.query.url!= "undefined" && req.query.url!=null){ - var user = req.query.user; - var url = req.query.url; - - //for the full AFIT rewards, grab only permalink portion without the community and author name - url = url.substring(url.lastIndexOf('/')+1); - console.log('url:'+url); - //grab specific reward type for user and post - var token_count = await getPostRewardFunc(user, url, "Full AFIT Payout"); - res.send({token_count: token_count}); - }else{ - res.send({token_count: 0}); - } -}); - - -/* end point for returning total number of rewarded tokens to charities based upon user activity, along with unique user count who donated */ -app.get('/getCharityRewards', async function(req, res) { - - await db.collection('token_transactions').aggregate([ - { - $match: {reward_activity:'Charity Post'} - }, - { - $group: - { - _id: null, - tokens_distributed: { $sum: "$token_count" }, - user_count: { $sum: 1 } - } - } - ]).toArray(function(err, results) { - var output = 'rewarded users:'+results[0].user_count+','; - output += 'tokens distributed:'+results[0].tokens_distributed; - res.send(results); - console.log(results); - }); - -}); - - - -/* end point for returning total number of AFIT tokens paid in return for full AFIT pay along with matching STEEM + SBD */ -app.get('/getFullAFITPayStats', async function(req, res) { - - await db.collection('token_transactions').aggregate([ - { - $match: {"reward_activity": "Full AFIT Payout"} - }, - { - $group: - { - _id: null, - afit_tokens: { $sum: "$token_count" }, - orig_sbd_amount: { $sum: "$orig_sbd_amount" }, - orig_steem_amount: { $sum: "$orig_steem_amount" }, - orig_sp_amount: { $sum: "$orig_sp_amount" }, - transaction_count: { $sum: 1 } - } - } - ]).toArray(function(err, results) { - res.send(results); - console.log(results); - }); - -}); - - -/* end point for capturing moderator activity on a specific date and for a specific period (defaults today and a single day activity) */ -app.get('/moderatorActivity', async function(req, res) { - let moderatorsList = await moderatorsListFunc(); - - //default today - var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - if (req.query.targetDate){ - startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); - } - //default single day - let days = 1; - if (!isNaN(req.query.days)){ - days = req.query.days; - } - var endDate = moment(moment(startDate).utc().subtract(days, 'days').toDate()).format('YYYY-MM-DD'); - console.log("startDate:"+startDate+" endDate:"+endDate); - - await db.collection('team').aggregate([ - { - $match: - { - title:'moderator', - status:'active' - } - }, - { - $lookup: - { - from: "token_transactions", - localField: "name", - foreignField: "user", - as: "moderatorActivity" - } - }, - { - $project: - { - '_id':0, - items: - { - $filter: { - input: "$moderatorActivity", - as: "singleEntry", - cond: { $and: [ - { "$lte": ["$$singleEntry.date", new Date(startDate)] }, - { "$gt": ["$$singleEntry.date", new Date(endDate)] } - ] } - } - } - } - } - ]).toArray(function(err, results) { - res.send(results); - console.log(results); - }); - -}); - -/* end point to grab current AFIT token price */ -app.get('/curAFITPrice', async function(req, res) { - let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next(); - console.log('curAfitPrice:'+curAFITPrice.unit_price_usd); - res.send(curAFITPrice); -}); - -/* handles the process of creating accounts*/ -proceedAccountCreation = async function (req){ - //let's create the account now - let accountCreated = false; - let transStored = false; - accountCreated = await utils.createAccount(req.query.new_account, req.query.new_pass); - if (accountCreated){ - transStored = await storeSignupTransaction(req); - //proceed only if a proper referrer was sent - if (typeof req.query.referrer != 'undefined' && req.query.referrer != 'undefined' && req.query.referrer != null){ - referralRewarded = await storeReferralReward(req); - } - } - console.log('account created:'+accountCreated); - return accountCreated; -} - -/* handles saving data related to signup to db */ -storeSignupTransaction = async function (req){ - console.log('reward new user'); - let result = false; - //setup new reward transaction for user - let new_transaction = { - account_name: req.query.new_account, - usd_invest: parseFloat(req.query.usd_invest), - steem_invest: parseFloat(req.query.steem_invest), - afit_reward: parseFloat(req.query.afit_reward), - memo: req.query.memo, - account_created: true, - payment_confirmed: true, - confirming_tx: req.query.confirming_tx, - date: new Date(), - } - - if (typeof req.query.referrer != 'undefined' && req.query.referrer != 'undefined' && req.query.referrer != null){ - new_transaction['referrer'] = req.query.referrer; - new_transaction['referrer_afit_reward'] = parseFloat(req.query.afit_reward * config.referrerBonus); - } - - if (typeof req.query.email != 'undefined' && req.query.email != 'undefined' && req.query.email != '' && req.query.email != null){ - new_transaction['email'] = req.query.email; - } - - //make sure we're not double storing referral - let query = { - account_name: req.query.new_account, - referrer: req.query.referrer, - }; - - try{ - let transaction = await db.collection('signup_transactions') - .replaceOne(query, new_transaction, { upsert: true }); - result = true; - }catch(e){ - console.log(e); - result = false; - } - - //also store this properly into user balance - - new_transaction = { - user: req.query.new_account, - reward_activity: 'Signup Reward', - token_count: parseFloat(req.query.afit_reward), - date: new Date(), - steem_invest: parseFloat(req.query.steem_invest), - usd_invest: parseFloat(req.query.usd_invest), - note: 'Successful Signup', - } - - //make sure we're not double rewarding user - query = { - user: req.query.new_account, - reward_activity: 'Signup Reward', - }; - - try{ - let transaction = db.collection('token_transactions') - .replaceOne(query, new_transaction, { upsert: true }); - result = true; - }catch(e){ - console.log(e); - result = false; - } - console.log(result); - return result; -} - - -/* function handles saving referral info and reward if the signup came through a referral */ -storeReferralReward = async function (req){ - console.log('reward referrer'); - //setup new reward transaction for user - let refRewarded = false; - let new_transaction = { - user: req.query.referrer, - reward_activity: 'Signup Referral', - token_count: parseFloat(req.query.afit_reward * config.referrerBonus), - date: new Date(), - referred: req.query.new_account, - note: 'Referral reward for signup of user '+req.query.new_account, - } - - //make sure we're not double rewarding user - let query = { - user: req.query.referrer, - reward_activity: 'Signup Referral', - referred: req.query.new_account, - }; - - try{ - let transaction = await db.collection('token_transactions') - .replaceOne(query, new_transaction, { upsert: true }); - refRewarded = true; - }catch(e){ - console.log(e); - } - - console.log('success'); - return refRewarded; -}; - -//function handles the process of confirming payment receipt, and then proceeds with account creation, reward and delegation -app.get('/confirmPayment', async function(req,res){ - if (req.query.confirm_payment_token != config.confirmPaymentToken){ - res.send('{}'); - }else{ - let paymentReceivedTx = ''; - let accountCreated = false; - let spToDelegate = 10; - //keeping request alive to avoid timeouts - let intID = setInterval(function(){ - res.write(' '); - }, 3000); - try{ - //first step is to ensure memo has not been tampered with, nor has it been claimed before - //to do that, let's try to find if any signup has been done using this memo - let memo_used = await db.collection('signup_transactions').findOne({memo: req.query.memo}); - console.log('memo_used:'+memo_used); - if (typeof memo_used == "undefined" || memo_used == null){ - paymentReceivedTx = await utils.confirmPaymentReceived(req); - console.log('>>>> got TX '+paymentReceivedTx); - if (paymentReceivedTx != ''){ - req.query.confirming_tx = paymentReceivedTx; - console.log(req.query); - try{ - accountCreated = await claimAndCreateAccount(req); - if (accountCreated){ - delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate); - } - }catch(e){ - console.log(e); - } - } - } - }catch(err){ - console.log(err); - } - //we're done, let's clear our running interval - clearInterval(intID); - //res.send({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated}); - res.write(JSON.stringify({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated})); - res.end(); - } -}); - -/* core function for discounted account claims and creation */ -claimAndCreateAccount = async function (req){ - let accountClaimed = false; - let accountCreated = false; - let results = ''; - try{ - results = await utils.getRC(config.account); - console.log('Current RC: ' + utils.format(results.estimated_pct) + '% '); - if (results.estimated_pct>50){ - //if we reached min threshold, claim more spots for discounted accounts - accountClaimed = await utils.claimDiscountedAccount(); - } - }catch(err){ - console.log('error grabbing RC'); - } - - console.log('discounted account claimed:'+accountClaimed); - //proceed creating account - try{ - accountCreated = await proceedAccountCreation(req); - }catch(err){ - console.log(err); - } - return accountCreated; - -}; - -function gk_add_commas(nStr) { - if (isNaN(nStr)){ - return nStr; - } - nStr += ''; - var x = nStr.split('.'); - var x1 = x[0]; - var x2 = x.length > 1 ? '.' + x[1] : ''; - var rgx = /(\d+)(\d{3})/; - while (rgx.test(x1)) { - x1 = x1.replace(rgx, '$1' + ',' + '$2'); - } - return x1 + x2; -} - -app.listen(appPort); +var express = require('express'); +var exphbs = require('express-handlebars'); +const MongoClient = require('mongodb').MongoClient; +var utils = require('./utils'); +const moment = require('moment') + +var appPort = process.env.PORT || 3120; + +var app = express(); + +app.engine('handlebars', exphbs({defaultLayout: 'main'})); +app.set('view engine', 'handlebars'); + +var config = utils.getConfig(); + +// Connection URL +let url = config.mongo_uri; +if (config.testing){ + url = config.mongo_local; +} + +var db; +var collection; +// Database Name +const db_name = config.db_name; +const collection_name = 'user_tokens'; + +// Use connect method to connect to the server +MongoClient.connect(url, function(err, client) { + if(!err) { + console.log("Connected successfully to server"); + + db = client.db(db_name); + + // Get the documents collection + collection = db.collection(collection_name); + } else { + utils.log(err, 'api'); + } + +}); + +//allows setting acceptable origins to be included across all function calls +app.use(function(req, res, next) { + var allowedOrigins = ['*', 'https://actifit.io', 'http://localhost:3000']; + var origin = req.headers.origin; + if(allowedOrigins.indexOf(origin) > -1){ + res.setHeader('Access-Control-Allow-Origin', origin); + } + return next(); +}); + +app.get('/', function (req, res) { + var data = {}; + data.posts = [ + { + url: 'dsadsa', + net_votes: 44, + vote_weight: "0.03" + }]; + data.total_votes = 323; + data.total_money = "$0.63"; + // res.render('home', data); + res.send('Hello there!'); +}); + + +/* function handles calculating and returning user token count */ +grabUserTokensFunc = async function (req, res){ + let user = await collection.findOne({_id: req.params.user}, {fields : { _id:0} }); + console.log(user); + //fixing token amount display for 3 digits + if (typeof user!= "undefined" && user!=null){ + if (typeof user.tokens!= "undefined"){ + user.tokens = user.tokens.toFixed(3) + } + } + return user; +} + +/* end point for user total token count display */ +app.get('/user/:user', async function (req, res) { + let user = await grabUserTokensFunc(req,res); + res.send(user); +}); + +/* end point for user transactions display (per user or general actifit token transactions, limited by 1000 */ +app.get('/transactions/:user?', async function (req, res) { + let query = {}; + var transactions; + if(req.params.user){ + query = {user: req.params.user} + transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + }else{ + //only limit returned transactions in case this is a general query + transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); + } + res.send(transactions); +}); + +/* end point for user referrals display (per user or general referrals */ +app.get('/signups/:user?', async function (req, res) { + let query = {account_created: true}; + var referrals; + if(req.params.user){ + query['referrer'] = req.params.user; + referrals = await db.collection('signup_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + }else{ + //only limit returned referrals in case this is a general query + referrals = await db.collection('signup_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); + } + res.send(referrals); +}); + +/* end point for returning number of awarded users and tokens distributed */ +app.get('/user-tokens-info', async function(req, res) { + + await db.collection(collection_name).aggregate([ + { + $match: {} + }, + { + $group: + { + _id: null, + tokens_distributed: { $sum: "$tokens" }, + user_count: { $sum: 1 } + } + } + ]).toArray(function(err, results) { + var output = 'rewarded users:'+results[0].user_count+','; + output += 'tokens distributed:'+results[0].tokens_distributed; + res.send(results); + console.log(results); + }); + +}); + + +/* end point for returning total delegation payments (number of delegators and amount paid) on a specific date */ +app.get('/delegationPayments', async function(req, res) { + var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + var dateRegex = todayDate; // /^2018-08-05/ + if (req.query.targetDate){ + dateRegex = req.query.targetDate; + } + + await db.collection('token_transactions').aggregate([ + { + $match: + { + "reward_activity": "Delegation", + "date": { + '$eq' : new Date(dateRegex) + } + } + }, + { + $group: + { + _id: null, + tokens_distributed: { $sum: "$token_count" }, + user_count: { $sum: 1 } + } + } + ]).toArray(function(err, results) { + res.send(results); + console.log(results); + }); + +}); + + +/* end point for returning total payments (categorized by reward type as well as a full total) on a specific date */ +app.get('/totalTokensDistributed', async function(req, res) { + + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + + await db.collection('token_transactions').aggregate([ + { + $match: + { + "date": { + "$lte": new Date(endDate), + "$gt": new Date(startDate) + } + } + }, + { + $group: + { + _id: {reward_activity:"$reward_activity"}, + tokens_distributed: { $sum: "$token_count" }, + } + } + ]).toArray(function(err, results) { + //also append total token count to the grouped display + let tot_tokens = 0; + for (let entry of results) { + tot_tokens += entry.tokens_distributed; + } + console.log(tot_tokens); + results.push([{"_id":null,"tokens_distributed":tot_tokens}]); + + res.send(results); + console.log(results); + }); + +}); + +/* end point for returning count of posts/activities rewarded */ +app.get('/rewarded-activity-count', async function(req, res) { + + await db.collection("posts").aggregate( [ + { $count: "reward_count" } + ]).toArray(function(err, results) { + console.log(results); + utils.log(results, 'rewarded-activity-count'); + res.send(results); + }); +}); + +/* end point for returning charity data supported by actifit */ +app.get('/charities', async function (req, res) { + var charities = await db.collection('available_charities').find({status:"enabled"}, {charity_name: 1}).sort({charity_name: 1}).toArray(); + res.send(charities); +}); + +/* end point for returning current active delegator data by actifit */ +app.get('/topDelegators', async function (req, res) { + var delegatorList; + if (isNaN(req.query.count)){ + delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).toArray(); + }else{ + delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).limit(parseInt(req.query.count)).toArray(); + } + res.send(delegatorList); +}); + +activeDelegationFunc = async function (userName){ + let user = await db.collection('active_delegations').findOne({_id: userName}, {fields : { _id:0} }); + console.log(user); + return user; +} + +/* end point for returning a single user last recorded active delegation amount */ +app.get('/delegation/:user', async function (req, res) { + var user = await activeDelegationFunc(req.params.user); + res.send(user); +}); + +moderatorsListFunc = async function () { + let moderatorList = await db.collection('team').find({title:'moderator', status:'active'}).sort({name: 1}).toArray(); + return moderatorList; +} + +/* end point for returning current active moderators data by actifit */ +app.get('/moderators', async function (req, res) { + var moderatorList; + moderatorList = await moderatorsListFunc(); + res.send(moderatorList); +}); + +/* end point for returning current active ambassadors data by actifit */ +app.get('/ambassadors', async function (req, res) { + var ambassadorList; + ambassadorList = await db.collection('team').find({title:'ambassador', status:'active'}).sort({name: 1}).toArray(); + res.send(ambassadorList); +}); + +/* end point for returning current top AFIT token holders */ +app.get('/topTokenHolders', async function (req, res) { + var tokenHolders; + if (isNaN(req.query.count)){ + tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).toArray(); + }else{ + tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); + } + let output = tokenHolders; + if (req.query.pretty){ + output = '#|Token Holder | AFIT Tokens Held |
'; + output += '|---|---|---|
'; + for(var i = 0; i < tokenHolders.length; i++) { + let tokenHolder = tokenHolders[i]; + output += (i+1) + '|'; + output += '@'+tokenHolder.user + '|'; + output += gk_add_commas(tokenHolder.tokens.toFixed(3)) + '|'; + output += '
'; + } + } + res.send(output); +}); + + +/* end point for returning accounts banned by actifit*/ +app.get('/banned_users', async function (req, res) { + var banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); + res.send(banned_users); +}); + + +/* end point for counting number of reblogs on a certain date param (default current date) */ +app.get('/reblogCount', async function (req, res) { + var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + //fileName = "steemrewards"+fileName+".json"; + var dateRegex = new RegExp ('^'+todayDate); // /^2018-08-05/ + if (req.query.targetDate){ + dateRegex = new RegExp ('^'+req.query.targetDate); + } + let query = await db.collection('token_transactions').find({ + "reward_activity": "Post Reblog", + "date": dateRegex + }) + try{ + console.log('counting'); + let reblog_count = await query.count(); + console.log(reblog_count); + res.send(JSON.stringify({reblog_count:reblog_count})); + }catch(err){ + console.log(err.message); + } +}); + +/* end point for counting number of upvotes on a certain date param (default current date) */ +app.get('/upvoteCount', async function (req, res) { + + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + //adjust query to include dates + query_json = { + "reward_activity": "Post Vote", + "date": { + "$lte": new Date(endDate), + "$gt": new Date(startDate) + } + }; + + let query = await db.collection('token_transactions').find(query_json); + + try{ + console.log('counting'); + let upvote_count = await query.count(); + console.log(upvote_count); + res.send(JSON.stringify({upvote_count:upvote_count})); + }catch(err){ + console.log(err.message); + } +}); + + +/* end point for counting number of rewarded posts on a certain date param (default current date) */ +app.get('/rewardedPostCount', async function (req, res) { + + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + //adjust query to include dates + query_json = { + "reward_activity": "Post", + "date": { + "$lte": new Date(endDate), + "$gt": new Date(startDate) + } + }; + + let query = await db.collection('token_transactions').find(query_json); + + try{ + console.log('counting'); + let rewarded_post_count = await query.count(); + console.log(rewarded_post_count); + res.send(JSON.stringify({rewarded_post_count:rewarded_post_count})); + }catch(err){ + console.log(err.message); + } +}); + +/* refactored function to grab rewarded post count per user for use across get calls */ +userRewardedPostCountFunc = async function(req, res){ + var user = req.params.user; + //default query + var query_json = { + "reward_activity": "Post", + "user": user + }; + //if this is a sum for specific period v/s a total sum + if (typeof req.query.period != "undefined" && !isNaN(req.query.period)){ + var days = req.query.period; + //console.log("days:"+days); + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + var endDate = moment(moment().utc().startOf('date').subtract(days, 'days').toDate()).format('YYYY-MM-DD'); + //console.log("startDate:"+startDate+" endDate:"+endDate); + //adjust query to include dates + query_json = { + "reward_activity": "Post", + "user": user, + "date": { + "$gte": new Date(endDate), + "$lt": new Date(startDate) + } + }; + } + + //build up query accordingly + let query = await db.collection('token_transactions').find(query_json); + try{ + //grab total number of matching records + let rewarded_post_count = await query.count(); + //console.log("rewarded_post_count:"+rewarded_post_count); + + return rewarded_post_count; + }catch(err){ + console.log(err.message); + return ""; + } +} + +/* end point for counting number of rewarded posts on a certain date param (default current date) */ +app.get('/userRewardedPostCount/:user', async function (req, res) { + + //grab user account + if (typeof req.params.user!= "undefined" && req.params.user!=null){ + + var rewarded_post_count = await userRewardedPostCountFunc(req, res); + res.send(JSON.stringify({rewarded_post_count:rewarded_post_count})); + }else{ + res.send(""); + } +}); + +/* end point for getting current user's Actifit rank */ +app.get('/getRank/:user', async function (req, res) { + + if (typeof req.params.user!= "undefined" && req.params.user!=null){ + + //delegation calculation matrix + var delegation_rules = [ + [9,0], + [499,0.05], + [999,0.10], + [4999,0.20], + [9999,0.30], + [19999,0.40], + [49999,0.55], + [99999,0.65], + [499999,0.75], + [999999,0.90], + [1000000,1] + ] + + //AFIT token calculation matrix + var afit_token_rules = [ + [9,0], + [999,0.10], + [4999,0.20], + [9999,0.30], + [19999,0.40], + [49999,0.50], + [99999,0.60], + [499999,0.70], + [999999,0.80], + [4999999,0.90], + [5000000,1] + ] + + //Rewarded Posts calculation matrix + var rewarded_posts_rules = [ + [9,0], + [29,0.10], + [59,0.20], + [89,0.30], + [119,0.40], + [179,0.50], + [359,0.60], + [539,0.70], + [719,0.80], + [1079,0.90], + [1080,1] + ] + + //Rewarded Posts calculation matrix + var recent_reward_posts_rules = [ + [0,0], + [2,0.20], + [4,0.40], + [6,0.60], + [8,0.80], + [9,1] + ] + + var user_rank = 0; + + //grab delegation amount + var userDelegations = await activeDelegationFunc(req.params.user); + + let delegSP = 0; + //get current delegated SP if any + if (userDelegations != null){ + console.log('already delegated'); + delegSP = userDelegations.steem_power; + } + //console.log(userDelegations.steem_power); + + var delegation_score = 0; + + //check if the user has an alt account as beneficiary + let delegator_info = await getAltAccountStatusFunc(req.params.user); + //check if returned object is not empty + if (Object.keys(delegator_info).length > 0){ + if (parseInt(delegator_info.user_rank_benefit) == 1){ + //consider as no delegations + delegSP = 0; + } + }else{ + //also check the other case where the account is an alt-account + delegator_info = await getAltAccountByNameFunc(req.params.user); + //check if returned object is not empty + if (Object.keys(delegator_info).length > 0){ + if (parseInt(delegator_info.user_rank_benefit) == 1){ + //get original user delegation amount + userDelegations = await activeDelegationFunc(delegator_info.delegator); + if (userDelegations != null){ + delegSP = userDelegations.steem_power; + } + } + } + } + + + if (parseFloat(delegSP) > 0){ + delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(delegSP)); + } + + user_rank += delegation_score; + + //grab user token count + var userTokens = await grabUserTokensFunc(req,res); + //console.log(userTokens.tokens); + + var afit_tokens_score = 0; + if (userTokens != null){ + afit_tokens_score = utils.calcScore(afit_token_rules, config.afit_token_factor, parseFloat(userTokens.tokens)); + } + + user_rank += afit_tokens_score; + + //grab total rewarded posts count + var tot_rewarded_post_count = await userRewardedPostCountFunc(req, res); + //console.log(tot_rewarded_post_count); + + var tot_posts_score = utils.calcScore(rewarded_posts_rules, config.rewarded_posts_factor, parseInt(tot_rewarded_post_count)); + + user_rank += tot_posts_score; + + //set the check period for config value of days days, and rerun the call to get last rewarded posting activity during this period + req.query.period = config.recent_posts_period; + + var recent_rewarded_post_count = await userRewardedPostCountFunc(req, res); + //console.log(recent_rewarded_post_count); + + var recent_posts_score = utils.calcScore(recent_reward_posts_rules, config.recent_posts_factor, parseInt(recent_rewarded_post_count)); + + user_rank += recent_posts_score; + + var score_components = JSON.stringify({ + user_rank: user_rank, + delegation_score: delegation_score, + afit_tokens_score: afit_tokens_score, + tot_posts_score: tot_posts_score, + recent_posts_score:recent_posts_score + }); + console.log(score_components) + + res.send(score_components); + }else{ + res.send(""); + } +}); + +/* function handles the backbone for grabbing Alt Account Status */ +getAltAccountStatusFunc = async function (user){ + let delegator_info = null; + if (typeof user!= "undefined" && user!=null){ + //in this case, we check the status of a single user + var query_json = { + "delegator": user + }; + + delegator_info = await db.collection('delegation_alt_beneficiaries').findOne(query_json, {fields : { _id:0} }); + if (delegator_info==null){ + delegator_info = {}; + } + console.log(delegator_info); + }else{ + //alternatively grab all alt-account reward delegations + delegator_info = await db.collection('delegation_alt_beneficiaries').find().toArray(); + console.log(delegator_info); + } + return delegator_info; +} + +/* function handles checking if alt-account is linked to a delegator */ +getAltAccountByNameFunc = async function (targetUser){ + let delegator_info = null; + if (typeof targetUser!= "undefined" && targetUser!=null){ + //in this case, we check the status of a single user + var query_json = { + "alt_account": targetUser + }; + + delegator_info = await db.collection('delegation_alt_beneficiaries').findOne(query_json, {fields : { _id:0} }); + if (delegator_info==null){ + delegator_info = {}; + } + console.log(delegator_info); + } + return delegator_info; +} + +/* end point for getting list of SP delegator accounts who wish to move their user rank and/or rewards to their alt-accounts*/ +app.get('/getAltAccountStatus/:user?', async function (req, res) { + let delegator_info = await getAltAccountStatusFunc(req.params.user); + res.send(delegator_info); +}); + +/* function handles processing requests for getting AFIT token pay depending on reward activity type */ +getPostRewardFunc = async function(user, url, reward_activity){ + var query_json = { + "reward_activity": reward_activity, + "user": user, + "url":url + }; + + let post_details = await db.collection('token_transactions').findOne(query_json, {fields : { _id:0} }); + console.log(post_details); + //fixing token amount display for 3 digits + if (typeof post_details!= "undefined" && post_details!=null){ + if (typeof post_details.token_count!= "undefined"){ + return post_details.token_count; + } + } + //otherwise return no tokens + return 0; +} + +/* end point for getting a post's reward */ +app.get('/getPostReward', async function (req, res) { + + if (typeof req.query.user!= "undefined" && req.query.user!=null + && typeof req.query.url!= "undefined" && req.query.url!=null){ + var user = req.query.user; + var url = req.query.url; + console.log('url:'+url); + //grab specific reward type for user and post + var token_count = await getPostRewardFunc(user, url, "Post"); + res.send({token_count: token_count}); + }else{ + res.send({token_count: 0}); + } +}); + +/* end point for retrieving a post's full AFIT Pay reward */ +app.get('/getPostFullAFITPayReward', async function (req, res) { + + if (typeof req.query.user!= "undefined" && req.query.user!=null + && typeof req.query.url!= "undefined" && req.query.url!=null){ + var user = req.query.user; + var url = req.query.url; + + //for the full AFIT rewards, grab only permalink portion without the community and author name + url = url.substring(url.lastIndexOf('/')+1); + console.log('url:'+url); + //grab specific reward type for user and post + var token_count = await getPostRewardFunc(user, url, "Full AFIT Payout"); + res.send({token_count: token_count}); + }else{ + res.send({token_count: 0}); + } +}); + + +/* end point for returning total number of rewarded tokens to charities based upon user activity, along with unique user count who donated */ +app.get('/getCharityRewards', async function(req, res) { + + await db.collection('token_transactions').aggregate([ + { + $match: {reward_activity:'Charity Post'} + }, + { + $group: + { + _id: null, + tokens_distributed: { $sum: "$token_count" }, + user_count: { $sum: 1 } + } + } + ]).toArray(function(err, results) { + var output = 'rewarded users:'+results[0].user_count+','; + output += 'tokens distributed:'+results[0].tokens_distributed; + res.send(results); + console.log(results); + }); + +}); + + + +/* end point for returning total number of AFIT tokens paid in return for full AFIT pay along with matching STEEM + SBD */ +app.get('/getFullAFITPayStats', async function(req, res) { + + await db.collection('token_transactions').aggregate([ + { + $match: {"reward_activity": "Full AFIT Payout"} + }, + { + $group: + { + _id: null, + afit_tokens: { $sum: "$token_count" }, + orig_sbd_amount: { $sum: "$orig_sbd_amount" }, + orig_steem_amount: { $sum: "$orig_steem_amount" }, + orig_sp_amount: { $sum: "$orig_sp_amount" }, + transaction_count: { $sum: 1 } + } + } + ]).toArray(function(err, results) { + res.send(results); + console.log(results); + }); + +}); + + +/* end point for capturing moderator activity on a specific date and for a specific period (defaults today and a single day activity) */ +app.get('/moderatorActivity', async function(req, res) { + let moderatorsList = await moderatorsListFunc(); + + //default today + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + //default single day + let days = 1; + if (!isNaN(req.query.days)){ + days = req.query.days; + } + var endDate = moment(moment(startDate).utc().subtract(days, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + + await db.collection('team').aggregate([ + { + $match: + { + title:'moderator', + status:'active' + } + }, + { + $lookup: + { + from: "token_transactions", + localField: "name", + foreignField: "user", + as: "moderatorActivity" + } + }, + { + $project: + { + '_id':0, + items: + { + $filter: { + input: "$moderatorActivity", + as: "singleEntry", + cond: { $and: [ + { "$lte": ["$$singleEntry.date", new Date(startDate)] }, + { "$gt": ["$$singleEntry.date", new Date(endDate)] } + ] } + } + } + } + } + ]).toArray(function(err, results) { + res.send(results); + console.log(results); + }); + +}); + +/* end point to grab current AFIT token price */ +app.get('/curAFITPrice', async function(req, res) { + let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next(); + console.log('curAfitPrice:'+curAFITPrice.unit_price_usd); + res.send(curAFITPrice); +}); + +/* handles the process of creating accounts*/ +proceedAccountCreation = async function (req){ + //let's create the account now + let accountCreated = false; + let transStored = false; + accountCreated = await utils.createAccount(req.query.new_account, req.query.new_pass); + if (accountCreated){ + transStored = await storeSignupTransaction(req); + //proceed only if a proper referrer was sent + if (typeof req.query.referrer != 'undefined' && req.query.referrer != 'undefined' && req.query.referrer != null){ + referralRewarded = await storeReferralReward(req); + } + } + console.log('account created:'+accountCreated); + return accountCreated; +} + +/* handles saving data related to signup to db */ +storeSignupTransaction = async function (req){ + console.log('reward new user'); + let result = false; + //setup new reward transaction for user + let new_transaction = { + account_name: req.query.new_account, + usd_invest: parseFloat(req.query.usd_invest), + steem_invest: parseFloat(req.query.steem_invest), + afit_reward: parseFloat(req.query.afit_reward), + memo: req.query.memo, + account_created: true, + payment_confirmed: true, + confirming_tx: req.query.confirming_tx, + date: new Date(), + } + + if (typeof req.query.referrer != 'undefined' && req.query.referrer != 'undefined' && req.query.referrer != null){ + new_transaction['referrer'] = req.query.referrer; + new_transaction['referrer_afit_reward'] = parseFloat(req.query.afit_reward * config.referrerBonus); + } + + if (typeof req.query.email != 'undefined' && req.query.email != 'undefined' && req.query.email != '' && req.query.email != null){ + new_transaction['email'] = req.query.email; + } + + //make sure we're not double storing referral + let query = { + account_name: req.query.new_account, + referrer: req.query.referrer, + }; + + try{ + let transaction = await db.collection('signup_transactions') + .replaceOne(query, new_transaction, { upsert: true }); + result = true; + }catch(e){ + console.log(e); + result = false; + } + + //also store this properly into user balance + + new_transaction = { + user: req.query.new_account, + reward_activity: 'Signup Reward', + token_count: parseFloat(req.query.afit_reward), + date: new Date(), + steem_invest: parseFloat(req.query.steem_invest), + usd_invest: parseFloat(req.query.usd_invest), + note: 'Successful Signup', + } + + //make sure we're not double rewarding user + query = { + user: req.query.new_account, + reward_activity: 'Signup Reward', + }; + + try{ + let transaction = db.collection('token_transactions') + .replaceOne(query, new_transaction, { upsert: true }); + result = true; + }catch(e){ + console.log(e); + result = false; + } + console.log(result); + return result; +} + + +/* function handles saving referral info and reward if the signup came through a referral */ +storeReferralReward = async function (req){ + console.log('reward referrer'); + //setup new reward transaction for user + let refRewarded = false; + let new_transaction = { + user: req.query.referrer, + reward_activity: 'Signup Referral', + token_count: parseFloat(req.query.afit_reward * config.referrerBonus), + date: new Date(), + referred: req.query.new_account, + note: 'Referral reward for signup of user '+req.query.new_account, + } + + //make sure we're not double rewarding user + let query = { + user: req.query.referrer, + reward_activity: 'Signup Referral', + referred: req.query.new_account, + }; + + try{ + let transaction = await db.collection('token_transactions') + .replaceOne(query, new_transaction, { upsert: true }); + refRewarded = true; + }catch(e){ + console.log(e); + } + + console.log('success'); + return refRewarded; +}; + +//function handles the process of confirming payment receipt, and then proceeds with account creation, reward and delegation +app.get('/confirmPayment', async function(req,res){ + if (req.query.confirm_payment_token != config.confirmPaymentToken){ + res.send('{}'); + }else{ + let paymentReceivedTx = ''; + let accountCreated = false; + let spToDelegate = 10; + //keeping request alive to avoid timeouts + let intID = setInterval(function(){ + res.write(' '); + }, 3000); + try{ + //first step is to ensure memo has not been tampered with, nor has it been claimed before + //to do that, let's try to find if any signup has been done using this memo + let memo_used = await db.collection('signup_transactions').findOne({memo: req.query.memo}); + console.log('memo_used:'+memo_used); + if (typeof memo_used == "undefined" || memo_used == null){ + paymentReceivedTx = await utils.confirmPaymentReceived(req); + console.log('>>>> got TX '+paymentReceivedTx); + if (paymentReceivedTx != ''){ + req.query.confirming_tx = paymentReceivedTx; + console.log(req.query); + try{ + accountCreated = await claimAndCreateAccount(req); + if (accountCreated){ + delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate); + } + }catch(e){ + console.log(e); + } + } + } + }catch(err){ + console.log(err); + } + //we're done, let's clear our running interval + clearInterval(intID); + //res.send({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated}); + res.write(JSON.stringify({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated})); + res.end(); + } +}); + +/* core function for discounted account claims and creation */ +claimAndCreateAccount = async function (req){ + let accountClaimed = false; + let accountCreated = false; + let results = ''; + try{ + results = await utils.getRC(config.account); + console.log('Current RC: ' + utils.format(results.estimated_pct) + '% '); + if (results.estimated_pct>50){ + //if we reached min threshold, claim more spots for discounted accounts + accountClaimed = await utils.claimDiscountedAccount(); + } + }catch(err){ + console.log('error grabbing RC'); + } + + console.log('discounted account claimed:'+accountClaimed); + //proceed creating account + try{ + accountCreated = await proceedAccountCreation(req); + }catch(err){ + console.log(err); + } + return accountCreated; + +}; + + +//function handles storing verified actifit posts to add additional security measures they came through our API and to avoid json metadata modifications +app.get('/appendVerifiedPost', async function(req,res){ + var passed_var = eval("req.query."+config.verifyParam); + //console.log(passed_var); + //make sure needed security var is passed, and with proper value + if ((typeof passed_var == 'undefined') || passed_var != config.verifyPostToken){ + res.send('{}'); + }else{ + let verified_post = { + author: req.query.author, + permlink: req.query.permlink, + json_metadata: JSON.parse(req.query.json_metadata), + }; + try{ + let transaction = await db.collection('verified_posts').insert(verified_post); + console.log('success inserting post data'); + res.send('{success}'); + }catch(err){ + console.log(err); + res.send('{error inserting post data}'); + } + } +}); + +//function handles checking and fetching a verified post +app.get('/fetchVerifiedPost', async function(req,res){ + //make sure we have both an author and permlink passed + if ((typeof req.query.author == 'undefined') || req.query.author == '' || + (typeof req.query.permlink == 'undefined') || req.query.permlink == '') { + res.send('{}'); + }else{ + try{ + let verified_post = await db.collection('verified_posts').findOne({author: req.query.author, permlink: req.query.permlink}); + console.log('found verified post'); + res.send(verified_post); + }catch(err){ + console.log(err); + res.send('{}'); + } + } +}); + +function gk_add_commas(nStr) { + if (isNaN(nStr)){ + return nStr; + } + nStr += ''; + var x = nStr.split('.'); + var x1 = x[0]; + var x2 = x.length > 1 ? '.' + x[1] : ''; + var rgx = /(\d+)(\d{3})/; + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + ',' + '$2'); + } + return x1 + x2; +} + +app.listen(appPort); From 5d09169020f36a03c63219ba14680839088f00fb Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 18 Jan 2019 01:15:14 +0200 Subject: [PATCH 064/193] Improve payments calculation Adjust the payments calculation to include an overall period, as well as average display within that period --- utils.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/utils.js b/utils.js index 1f4f376..3ebe17b 100644 --- a/utils.js +++ b/utils.js @@ -601,8 +601,6 @@ function resetVals(){ producerSPRewards = 0 } -lookupAccountPay(); - async function lookupAccountPay (){ const ONE_DAY = 1; From 660de7938ca429ae5255385e2ef3ffb0754bf48b Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 18 Jan 2019 16:29:06 +0200 Subject: [PATCH 065/193] Add new top query limit Implement a new setting that allows setting a limit upon the maximum number of blockchain queries to be called while fetching available reports. --- curation-bot.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/curation-bot.js b/curation-bot.js index 2e49147..b620e4f 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -197,6 +197,8 @@ if (!config.testing){ var votePosts; var lastIterationCount = 0; +let queryCount = 0; + async function startProcess() { if(!botNames) botNames = await utils.loadBots(); @@ -297,6 +299,8 @@ function processVotes(query, subsequent) { utils.log('processVotes'); steem.api.getDiscussionsByCreated(query, async function (err, result) { + //track how many queries were ran + queryCount += 1; if (result && !err) { is_voting = true; @@ -759,7 +763,7 @@ function processVotes(query, subsequent) { //if this is the first try, or the new count of posts is bigger than the one before, let's try adding again - if (!subsequent || votePosts.length>lastIterationCount){ + if (!subsequent || votePosts.length > lastIterationCount || queryCount < config.max_query_count){ //update last count lastIterationCount = votePosts.length; From 7ac3f1bb8a3c0007c46bf7779bdc701806dc5d8b Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 19 Jan 2019 00:21:56 +0200 Subject: [PATCH 066/193] Implement checks against altered critical data --- curation-bot.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/curation-bot.js b/curation-bot.js index b620e4f..244ffb6 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -343,7 +343,9 @@ function processVotes(query, subsequent) { utils.log('Bot already voted on: ' + post.url); continue; } - + + //post.json_metadata = JSON.parse(body); + // Check if any tags on this post are blacklisted in the settings if ((config.blacklisted_tags && config.blacklisted_tags.length > 0) || (config.whitelisted_tags && config.whitelisted_tags.length > 0) && post.json_metadata && post.json_metadata != '') { var tags = JSON.parse(post.json_metadata).tags; @@ -414,9 +416,96 @@ function processVotes(query, subsequent) { continue; } + + try { post.json = JSON.parse(post.json_metadata); + + //we need to fetch the proper json_metadata from our own DB to ensure those have not been changed + + let ver_url = config.api_url + "fetchVerifiedPost"; + let critical_fields = ['step_count', 'actiCrVal', 'actifitUserID']; + + let incons_detected = false; + let incons_field = ''; + + try{ + var verf_res = await axios.get(ver_url, { + params:{ + author: post.author, + permlink: post.permlink + } + }); + + //let's compare mission critical data to find if manipulation was done + let auth_meta = verf_res.data.json_metadata; + + //if either stored or current metadata is non-empty we need to investigate further + if (auth_meta != '' || post.json_metadata != ''){ + //check all critical values + critical_fields.some(function(element) { + //initialize incons field in case we find a match (or lack of) + incons_field = element; + let stored_meta = eval("auth_meta."+element); + let new_meta = eval("post.json."+element); + /*console.log('stored_meta'); + console.log(stored_meta); + console.log('new_meta'); + console.log(new_meta);*/ + //if old data is not empty + if (typeof stored_meta != 'undefined' && stored_meta != ''){ + if (stored_meta instanceof Array ){ + if (new_meta instanceof Array){ + //our arrays are single valued, compare first entry + if (stored_meta[0] != new_meta[0]){ + //different value, manipulation + incons_detected = true; + return true; + } + }else{ + //different object types, manipulation + incons_detected = true; + return true; + } + }else{ + if (stored_meta != new_meta){ + //different value, manipulation + incons_detected = true; + return true; + } + } + }else{ + //original data is empty, need to check if new data is not + if (typeof new_meta != 'undefined' && new_meta != ''){ + incons_detected = true; + return true; + } + } + }); + } + }catch(verf_err){ + console.log('error finding matching post on DB'); + console.dir(verf_err); + } + console.log('data inconsistency: ' + incons_detected); + //check if we found metadata issue + if (incons_detected){ + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('problematic field:' + incons_field); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + //we've got a problem, skip this post/guy. We might want to report too. + continue; + } + //check if the post has an encryption key val, and ensure it is the proper one From 1e1878c59ca737ceadba2a6c3cf2bbad7616024b Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 21 Jan 2019 00:16:59 +0200 Subject: [PATCH 067/193] Fix charity total count issue Fix issue with charity user being stored as an array, causing problems with data display and calculation of the total AFIT tokens --- curation-bot.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 244ffb6..45a907f 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -179,6 +179,7 @@ MongoClient.connect(url, function(err, client) { collection = db.collection(collection_name); //only start the process once we connected to the DB startProcess(); + //updateUserTokens(); } else { utils.log(err, 'api'); } @@ -738,7 +739,7 @@ function processVotes(query, subsequent) { var result; //if we find this is a charity run, let's switch it to the actual charity name if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ - reward_user = post.json.charity; + reward_user = post.json.charity[0]; activity_type = 'Charity Post'; note = 'Charity donation via activity by user '+post.author; } @@ -893,7 +894,7 @@ function processVotes(query, subsequent) { //if we find this is a charity run, let's switch it to the actual charity name if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ - reward_user = post.json.charity; + reward_user = post.json.charity[0]; activity_type = 'Charity Post'; note = 'Charity donation via actifit post by user '+post.author; } @@ -1074,7 +1075,7 @@ function sendVote(post, retries, power_per_vote) { //function handles updating current user token count async function updateUserTokens() { utils.log('---- Updating Users ----'); - + let insert_res try{ //group all token transactions per user, and sum them to generate new total count let query = await db.collection('token_transactions').aggregate([ @@ -1089,11 +1090,18 @@ async function updateUserTokens() { ]) let user_tokens = await query.toArray(); + console.log('grab query all items'); //remove old token count per user await db.collection('user_tokens').remove({}); + console.log('removed all entries'); + console.log(user_tokens); //insert new count per user - await db.collection('user_tokens').insert(user_tokens); + insert_res = await db.collection('user_tokens').insertMany(user_tokens); + console.log(insert_res.insertedCount); + console.log('inserted all entries'); }catch(err){ + console.log(insert_res); + console.log(err); utils.log('>>save data error:'+err.message); } } From 13b78af7c74342a301978cb95d5f08d3febaa8fb Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 21 Jan 2019 22:20:08 +0200 Subject: [PATCH 068/193] Implement fix to missing voting date Recent changes by Steemit inc caused their node no longer to provide voting date, which caused votes to default to 1970. We implemented a fix to utilize post date i/o missing voting date --- curation-bot.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/curation-bot.js b/curation-bot.js index 45a907f..3b7ee77 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -801,6 +801,7 @@ function processVotes(query, subsequent) { //utils.log(post.active_votes); post.active_votes.forEach(async vote => { + console.log(vote); //grab user's contribution to the upvote pool var upv_tokens = parseInt(vote.rshares); @@ -811,12 +812,17 @@ function processVotes(query, subsequent) { var voter_tokens = upv_tokens / total_post_upv_shares * max_afits; //console.log(voter_tokens); voter_tokens = parseFloat(voter_tokens.toFixed(3)); + let used_date = vote.time; + if (used_date == undefined || used_date == ''){ + used_date = post.created; + } + console.log(used_date); let vote_transaction = { user: vote.voter, reward_activity: 'Post Vote', token_count: voter_tokens, url: post.url, - date: new Date(vote.time) + date: new Date(used_date)//vote.time) } bulk_transactions.find( { From 56458ff1304570bb46ca871e45544ed8903c348c Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 25 Jan 2019 23:51:35 +0200 Subject: [PATCH 069/193] Implement Permanent Skippable Posts Implement measure to prevent date skipped posts from receiving future round rewards --- curation-bot.js | 157 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 105 insertions(+), 52 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 3b7ee77..d54e607 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -144,6 +144,8 @@ var banned_users; var moderator_list; +var skippable_posts; + // Check if bot state has been saved to disk, in which case load it if (fs.existsSync('state.json')) { var state = JSON.parse(fs.readFileSync("state.json")); @@ -270,6 +272,12 @@ async function startProcess() { moderator_list.push(moderator_array[mod_it].name); } utils.log(moderator_list); + + //grab list of skippable posts + skippable_posts = await db.collection('posts_to_skip').find().toArray(); + + console.log('skippable_posts'); + console.log(skippable_posts); var query = {tag: config.main_tag, limit: 100}; votePosts = Array(); @@ -314,6 +322,9 @@ function processVotes(query, subsequent) { //connect to the token_transactions table to start rewarding var bulk_transactions = db.collection('token_transactions').initializeUnorderedBulkOp(); + var bulk_posts_skip = db.collection('posts_to_skip').initializeUnorderedBulkOp(); + + for(var i = 0; i < result.length; i++) { var post = result[i]; @@ -412,12 +423,22 @@ function processVotes(query, subsequent) { } if (user_banned) continue; - //skip any posts that are more than 1.5 days old + //skip any posts that are more than max days old if((new Date() - new Date(post.created + 'Z')) >= (config.max_days * 24 * 60 * 60 * 1000)) { continue; } + //skip any posts that are flagged as skippable in prior iterations + var post_skippable = false; + for (var n = 0; n < skippable_posts.length; n++) { + if (post.author == skippable_posts[n].author && post.permlink == skippable_posts[n].permlink){ + utils.log('>>>>>>>Post by '+post.author+' is skippable, move ahead:' + post.url); + post_skippable = true; + break; + } + } + if (post_skippable) continue; try { @@ -489,7 +510,7 @@ function processVotes(query, subsequent) { console.log('error finding matching post on DB'); console.dir(verf_err); } - console.log('data inconsistency: ' + incons_detected); + //console.log('data inconsistency: ' + incons_detected); //check if we found metadata issue if (incons_detected){ console.log('***********************'); @@ -526,6 +547,80 @@ function processVotes(query, subsequent) { utils.log('post does not contain actiCrVal'); continue; } + + + //moving this section before the actual token rewards + + //due to the difference in server times, a user's post might have same date created. + //to avoid this issue, we will accept 2 posts for every user + //so we will check if 2 posts are already accumulated for the user, and if so reject the third + + let last_index = _.findLastIndex(votePosts, ['author', post.author]); + let first_index = _.findIndex(votePosts, ['author', post.author]); + let skip_date_diff = false; + + if (last_index != -1 && (first_index!=last_index)) { + utils.log('---- User already has more than 2 posts in 24 hours ------'); + let last_voted = votePosts[last_index]; + var last_date = moment(last_voted.created).format('D'); + let first_voted = votePosts[first_index]; + var first_date = moment(first_voted.created).format('D'); + var this_date = moment(post.created).format('D'); + //if all 3 dates match, skip it + if ((last_date == this_date) && (first_date == this_date)) { + utils.log('---- Last voted -----'); + utils.log(new Date (last_voted.created)); + utils.log('---- First voted -----'); + utils.log(new Date (first_voted.created)); + utils.log('---- This voted -----'); + utils.log(new Date (post.created)); + utils.log('---- Moment-----'); + utils.log(last_date); + utils.log(first_date); + utils.log(this_date); + + //skip new post + skip_date_diff = true; + + } + }else if (last_index != -1){ + utils.log('last_index:'+last_index); + utils.log(post.author+post.url); + //adding condition to reject a post if a prior one exists that is less than 6 hours away + let last_voted = votePosts[last_index]; + //utils.log(last_voted.author+last_voted.url); + var last_date = moment(last_voted.created).toDate(); + var this_date = moment(post.created).toDate(); + //check the hours difference + var hours_diff = Math.abs(this_date - last_date) / 36e5; + if (hours_diff { - console.log(vote); //grab user's contribution to the upvote pool var upv_tokens = parseInt(vote.rshares); @@ -816,7 +863,6 @@ function processVotes(query, subsequent) { if (used_date == undefined || used_date == ''){ used_date = post.created; } - console.log(used_date); let vote_transaction = { user: vote.voter, reward_activity: 'Post Vote', @@ -842,6 +888,13 @@ function processVotes(query, subsequent) { } }//end of loop going through posts + //properly stored any future skippable posts + try{ + await bulk_posts_skip.execute(); + }catch(bulkerr){ + utils.log(bulkerr); + } + if (votePosts.length>0){ try{ //store posts From b29848b193ce105610a233480768af93f3f76e19 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Tue, 5 Feb 2019 15:06:41 +0200 Subject: [PATCH 070/193] Add endpoint to fetch categorized token holders New endpoint that allows fetching token holders categorized by the range of their tokens held --- app.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/app.js b/app.js index 51810e7..2c5eceb 100644 --- a/app.js +++ b/app.js @@ -1044,6 +1044,58 @@ app.get('/fetchVerifiedPost', async function(req,res){ } }); +/* end point handling the display of categorized token holders */ +app.get('/fetchTokenHoldersByCategory', async function(req, res){ + //connect to DB, and identify token holders by category + await db.collection('user_tokens').aggregate([ + { + $project: { + "range": { + $concat: [ + { $cond: [{$lt: ["$tokens",1]}, "< 1 AFIT", ""]}, + { $cond: [{$and:[ {$gte:["$tokens", 1 ]}, {$lt: ["$tokens", 11]}]}, "1 - 10 AFIT", ""] }, + { $cond: [{$and:[ {$gte:["$tokens",11]}, {$lt:["$tokens", 101]}]}, "11 - 100 AFIT", ""]}, + { $cond: [{$and:[ {$gte:["$tokens",101]}, {$lt:["$tokens", 1001]}]}, "101 - 1,000 AFIT", ""]}, + { $cond: [{$and:[ {$gte:["$tokens",1001]}, {$lt:["$tokens", 10001]}]}, "1,001 - 10,000 AFIT", ""]}, + { $cond: [{$and:[ {$gte:["$tokens",10001]}, {$lt:["$tokens", 50001]}]}, "10,001 - 50,000 AFIT", ""]}, + { $cond: [{$and:[ {$gte:["$tokens",50001]}, {$lt:["$tokens", 100001]}]}, "50,001 - 100,000 AFIT", ""]}, + { $cond: [{$and:[ {$gte:["$tokens",100001]}, {$lt:["$tokens", 500001]}]}, "100,001 - 500,000 AFIT", ""]}, + { $cond: [{$and:[ {$gte:["$tokens",500001]}, {$lt:["$tokens", 1000001]}]}, "500,001 - 1,000,000 AFIT", ""]}, + { $cond: [{$gte:["$tokens",1000001]}, "> 1,000,000 AFIT", ""]} + ] + } + } + }, + { + $group: { + "_id" : "$range", + count: { + $sum: 1 + } + } + }, + { + $sort: { + "count": -1, + } + } + ]).toArray(function(err, results) { + if (req.query.pretty==1){ + let output = '|Category|Count|
'; + output += '|---|---|
'; + for (let entry of results) { + output += '|' + entry._id + '|' + entry.count + '
'; + } + res.send(output); + }else{ + res.send(results); + } + }); + +}); + + + function gk_add_commas(nStr) { if (isNaN(nStr)){ return nStr; From 3b0ed54a0a75e0930105fdc504703a501f98eb94 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 10 Feb 2019 23:59:01 +0200 Subject: [PATCH 071/193] Add New Reward Types Endpoints Created 2 new endpoints to allow new types of rewards for users: - Web Vote: which handles rewarding a user who voted using our web interface - Web Edit: which handles rewarding a user who made an edit on our web interface --- app.js | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/app.js b/app.js index 2c5eceb..117f0f0 100644 --- a/app.js +++ b/app.js @@ -1095,6 +1095,79 @@ app.get('/fetchTokenHoldersByCategory', async function(req, res){ }); +/* end point handling additional reward to user votes via web */ +app.get('/rewardActifitWebVote/:user', async function(req,res){ + if (req.query.web_vote_token != config.actifitWebVoteToken){ + res.send('{}'); + }else{ + let reward_activity = 'Web Vote'; + let rewarded = await rewardActifitTokenWeb(req, reward_activity); + res.send({'rewarded':rewarded, amount: config.actifitWebVoteRewardAmount}); + } +}); + +/* end point handling rewarding web edits */ +app.get('/rewardActifitWebEdit/:user', async function(req,res){ + if (req.query.web_edit_token != config.actifitWebEditToken){ + res.send('{}'); + }else{ + let reward_activity = 'Web Edit'; + let rewarded = await rewardActifitTokenWeb(req, reward_activity); + res.send({'rewarded':rewarded, amount: config.actifitWebEditRewardAmount}); + } +}); + +/* core function handling user rewards for various web related activities */ +rewardActifitTokenWeb = async function (req, reward_activity) { + //store outcome + let rewarded = false; + + //make sure we have user and url params set + if (req.params.user && typeof req.query.url!= "undefined" && req.query.url!=null) { + try{ + //let's reward this user for performing an edit using our web interface + let reward_date = new Date(); + + //only one reward per day, disregard time + reward_date.setHours(0,0,0,0); + + let new_transaction = { + user: req.params.user, + reward_activity: reward_activity, + token_count: parseFloat(config.actifitWebEditRewardAmount), + date: new Date(), + reward_date: reward_date, + url: req.query.url, + } + + //make sure we're not double rewarding user. New url edits override older ones on same date + let query = { + user: req.params.user, + reward_activity: reward_activity, + reward_date: reward_date, + }; + + //check if we have a match already to skip rewarding and/or notifying the user + let user_pre_rewarded = await db.collection('token_transactions').findOne(query); + if (typeof user_pre_rewarded == undefined || user_pre_rewarded == 'undefined' || user_pre_rewarded == null){ + console.log('first reward today'); + try{ + let transaction = db.collection('token_transactions') + .replaceOne(query, new_transaction, { upsert: true }); + rewarded = true; + }catch(e){ + console.log(e); + } + } + console.log(rewarded); + + }catch(err){ + console.log(err); + } + } + + return rewarded; +} function gk_add_commas(nStr) { if (isNaN(nStr)){ From 053e957ef0f9ed3b8eeb0c94d9849195b222c026 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 11 Feb 2019 00:00:07 +0200 Subject: [PATCH 072/193] Upgrade Vulnerable Package Upgrade lodash version due to reported vulnerability --- package.json | 79 ++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index a2a1211..f21c7da 100644 --- a/package.json +++ b/package.json @@ -1,42 +1,37 @@ -{ - "name": "actifit-curation-bot", - "version": "0.0.1", - "description": "actifit curation bot", - "main": "curation-bot.js", - "dependencies": { - "axios": "^0.18.0", - "dsteem": "^0.9.0", - "express": "^4.16.2", - "lodash": "^4.17.10", - "moment": "^2.22.2", - "mongodb": "^3.0.10", - "node-schedule": "^1.3.0", - "nodemailer": "^4.6.5", - "nodemailer-express-handlebars": "^3.0.0", - "p-iteration": "^1.1.7", - "request": "^2.83.0", - "steem": "^0.6.7" - }, - "devDependencies": { - "eslint": "^5.2.0", - "eslint-config-standard": "^11.0.0", - "eslint-plugin-import": "^2.13.0", - "eslint-plugin-node": "^7.0.1", - "eslint-plugin-promise": "^3.8.0", - "eslint-plugin-standard": "^3.1.0", - "nodemon": "^1.17.5" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "curate": "node curation-bot.js", - "import": "node save-data.js", - "api": "node app.js", - "all": "node app.js | node save-data.js | node curation-bot.js", - "start": "node app.js | node save-data.js | node curation-bot.js" - }, - "engines": { - "node": "10.3.0" - }, - "author": "cryptouru", - "license": "MIT" -} +{ + "name": "actifit-curation-bot", + "version": "0.0.1", + "description": "actifit curation bot", + "main": "curation-bot.js", + "dependencies": { + "axios": "^0.18.0", + "cheerio": "^1.0.0-rc.2", + "dsteem": "^0.10.1", + "express": "^4.16.2", + "jquery": "^3.3.1", + "lodash": ">=4.17.11", + "moment": "^2.22.2", + "mongodb": "^3.0.10", + "node-schedule": "^1.3.0", + "nodemailer": "^4.6.5", + "nodemailer-express-handlebars": "^3.0.0", + "p-iteration": "^1.1.7", + "request": "^2.88.0", + "steem": "^0.6.7" + }, + "devDependencies": { + "nodemon": "^1.17.5" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "delegate": "node delegations.js", + "api": "node app.js", + "all": "node app.js | node delegations.js ", + "start": "node app.js | node delegations.js " + }, + "engines": { + "node": "10.3.0" + }, + "author": "cryptouru", + "license": "MIT" +} From 932a20218cb515815a761378fc97fc9de08e17bc Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 9 Mar 2019 20:22:31 +0200 Subject: [PATCH 073/193] Additional Reward Types + Payment Verification - Implement additional reward calculation components when fetching historical data (author_reward + transfer) - Enable larger historical data up to 1 year - Implement new functionality to confirm payments received for exchange functionality - Fix issue with potential loophole for prior payments on signup --- utils.js | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 137 insertions(+), 9 deletions(-) diff --git a/utils.js b/utils.js index 3ebe17b..5289576 100644 --- a/utils.js +++ b/utils.js @@ -23,6 +23,8 @@ var HOURS = 60 * 60; var votePowerReserveRate; var totalVestingFund; var totalVestingShares; + var steem_per_mvests; + var sbd_print_percentage; var botNames; let properties let totalVests @@ -41,6 +43,10 @@ var HOURS = 60 * 60; votePowerReserveRate = t.vote_power_reserve_rate; totalVestingFund = parseFloat(t.total_vesting_fund_steem.replace(" STEEM", "")); totalVestingShares = parseFloat(t.total_vesting_shares.replace(" VESTS", "")); + steem_per_mvests = ((totalVestingFund / totalVestingShares) * 1000000); + sbd_print_percentage = t.sbd_print_rate / 10000 + console.log(steem_per_mvests); + console.log(sbd_print_percentage); }); setTimeout(updateSteemVariables, 180 * 1000) @@ -121,9 +127,17 @@ var HOURS = 60 * 60; if (op[1].to === config.signup_account && op[1].memo === req.query.memo && sentAmount >= (parseFloat(req.query.steem_invest)-0.1)){ console.log('in'); console.log(op[1]); - tx_id = txs[1].trx_id; - paymentFound = true; - break; + + let now = moment(new Date()); //todays date + let end = moment(txs[1].timestamp); // last update date + let duration = moment.duration(now.diff(end)); + let hrs = duration.asHours(); + //transaction needs to have been concluded within 5 hours. + if (hrs < 5){ + tx_id = txs[1].trx_id; + paymentFound = true; + break; + } } } } @@ -138,6 +152,51 @@ var HOURS = 60 * 60; }); } + //function handles confirming if payment was received + async function confirmPaymentReceivedPassword (req) { + getConfig(); + console.log('confirmPaymentReceivedPassword'); + return new Promise((resolve, reject) => { + let th_id = setInterval(async function(){ + console.log('check funds'); + steem.api.getAccountHistory(config.exchange_account, -1, 300, (err, transactions) => { + let tx_id = ''; + let paymentFound = false; + for (let txs of transactions) { + let op = txs[1].op + //check if we received a transfer to our target account + //if we found a transfer operation sent to our target account, with the correct memo and the proper amount, proceed + if (op[0] === 'transfer'){ + let sentAmount = op[1].amount.split(' ')[0]; + if (op[1].to === config.exchange_account && op[1].from === req.query.from && sentAmount >= 1){ + console.log('in'); + console.log(op[1]); + //console.log(txs); + /*let now = moment(new Date()); //todays date + let end = moment(txs[1].timestamp); // last update date + let duration = moment.duration(now.diff(end)); + let hrs = duration.asHours(); + //transaction needs to have been concluded within 5 hours. + if (hrs < 24){*/ + tx_id = txs[1].trx_id; + paymentFound = true; + break; + //} + } + } + } + if (paymentFound){ + //need to look again + console.log('found'); + clearInterval(th_id); + resolve(tx_id); + } + }); + }, 5000); + }); + } + + //function handles claiming spots for accounts async function claimDiscountedAccount(){ console.log('claimDiscountedAccount'); @@ -326,7 +385,7 @@ var HOURS = 60 * 60; } } - function getVoteValue(voteWeight, account, power) { + function getVoteValue(voteWeight, account, power, steem_price) { if (!account) { return; } @@ -340,6 +399,14 @@ var HOURS = 60 * 60; } } + function getVoteValueUSD(vote_value, sbd_price) { + const steempower_value = vote_value * 0.5 + const sbd_print_percentage_half = (0.5 * sbd_print_percentage) + const sbd_value = vote_value * sbd_print_percentage_half + const steem_value = vote_value * (0.5 - sbd_print_percentage_half) + return (sbd_value * sbd_price) + steem_value + steempower_value + } + function timeTilFullPower(cur_power){ return (STEEMIT_100_PERCENT - cur_power) * STEEMIT_VOTE_REGENERATION_SECONDS / STEEMIT_100_PERCENT; } @@ -585,6 +652,14 @@ let curTotalSp = 0 let curTotalSTEEM = 0 let producerSPRewards = 0 + +let authorRewardedSBD = 0 +let authorRewardedSp = 0 +let authorRewardedSTEEM = 0 + +let accountSTEEMTransfer = 0; +let accountSBDTransfer = 0; + let limit = 5000; let txStart = -1; let opsArr = []; @@ -599,16 +674,26 @@ function resetVals(){ curTotalSTEEM = 0 producerSPRewards = 0 + + authorRewardedSBD = 0 + authorRewardedSp = 0 + authorRewardedSTEEM = 0 + + accountSTEEMTransfer = 0 + accountSBDTransfer = 0 } +//lookupAccountPay(); + async function lookupAccountPay (){ const ONE_DAY = 1; const ONE_WEEK = 7; const ONE_MONTH = 30; + const ONE_YEAR = 365; let start_days = 1; - let lookup_days = ONE_WEEK; + let lookup_days = ONE_YEAR; let today = moment().utc().startOf('date').toDate() let start = moment(today).subtract(start_days, 'days').toDate() @@ -624,6 +709,10 @@ async function lookupAccountPay (){ txStart = -1; console.log('***********append actifit.funds rewards**********') await getAccountPayTransactions('actifit.funds', start, to, lookup_days); + + txStart = -1; + console.log('***********append actifit.pay rewards**********') + await getAccountPayTransactions('actifit.pay', start, to, lookup_days); } async function getAccountPayTransactions (account, start, end, period) { @@ -704,6 +793,28 @@ async function getAccountPayTransactions (account, start, end, period) { //console.log("rewardedSBD:"+rewardedSBD); //curTotalSBD += rewardedSBD; + }else if (op[0] === 'author_reward') { + //console.log('caught one author_reward'); + //console.log(op); + + authorRewardedSBD += parseFloat(op[1].sbd_payout.split(' ')[0]) + authorRewardedSp += parseFloat(vestsToSteemPower(op[1].vesting_payout.split(' ')[0]).toFixed(3)) + authorRewardedSTEEM += parseFloat((op[1].steem_payout.split(' ')[0])) + }else if (op[0] === 'comment_reward') { + console.log('comment_reward FOUND'); + console.log(op); + }else if (op[0] === 'transfer' && op[1].from === account && + (op[1].to !== 'bittrex' && !op[1].to.includes('actifit'))){//skip bittrex and actifit account transfers + console.log('>>>>>><<<<<<<'); + console.log(op[1]); + let amountWithCur = op[1].amount; + let amount = amountWithCur.split(' ')[0] + let cur = amountWithCur.split(' ')[1] + if (cur == 'SBD'){ + accountSBDTransfer += parseFloat(amount) + }else{ + accountSTEEMTransfer += parseFloat(amount) + } } } else if (date < end){ break @@ -738,6 +849,15 @@ async function getAccountPayTransactions (account, start, end, period) { if (period>0 && curTotalSp>0){ console.log ('AVG Daily:'+curTotalSp/period); } + + console.log ('---author---'); + console.log ('totalSP:'+authorRewardedSp); + console.log ('total_STEEM:'+authorRewardedSTEEM); + console.log ('totalSBD:'+authorRewardedSBD); + if (period>0 && curTotalSp>0){ + console.log ('AVG Daily:'+curTotalSp/period); + } + //console.log ('totalSTEEM:'+curTotalSTEEM); // console.log ('totalSBD:'+curTotalSBD); console.log ('---witness---'); @@ -746,16 +866,21 @@ async function getAccountPayTransactions (account, start, end, period) { console.log ('AVG Daily:'+producerSPRewards/period); } console.log ('---totals---'); - let comSP = parseFloat(totalSp.toFixed(3))+parseFloat(curTotalSp.toFixed(3))+parseFloat(producerSPRewards.toFixed(3))+parseFloat(total_STEEM.toFixed(3)); + let comSP = parseFloat(totalSp.toFixed(3))+parseFloat(curTotalSp.toFixed(3))+parseFloat(producerSPRewards.toFixed(3))+parseFloat(total_STEEM.toFixed(3)) + + parseFloat(authorRewardedSp.toFixed(3))+parseFloat(authorRewardedSTEEM.toFixed(3)); console.log ('totalSTEEM:'+comSP.toFixed(3)); if (period>0 && comSP>0){ console.log ('AVG Daily:'+comSP/period); } //console.log ('totalSTEEM:'+totalSTEEM.toFixed(3)); - console.log ('totalSBD:'+totalSBD.toFixed(3)); - if (period>0 && totalSBD>0){ - console.log ('AVG Daily:'+totalSBD/period); + let comSBD = parseFloat(totalSBD.toFixed(3))+parseFloat(authorRewardedSBD.toFixed(3)); + console.log ('totalSBD:'+comSBD); + if (period>0 && comSBD>0){ + console.log ('AVG Daily:'+comSBD/period); } + console.log ('---delegator pay---'); + console.log ('delegatorPaySTEEM:'+accountSTEEMTransfer); + console.log ('delegatorPaySBD:'+accountSBDTransfer); console.log ('------------'); //console.log (opsArr); } @@ -780,9 +905,11 @@ async function steemPowerToVests (steemPower) { module.exports = { + updateSteemVariables: updateSteemVariables, getVotingPower: getVotingPower, getRC: getRC, claimDiscountedAccount: claimDiscountedAccount, + getVoteValueUSD: getVoteValueUSD, getVoteValue: getVoteValue, timeTilFullPower: timeTilFullPower, getVestingShares: getVestingShares, @@ -804,4 +931,5 @@ async function steemPowerToVests (steemPower) { createAccount: createAccount, delegateToAccount: delegateToAccount, confirmPaymentReceived: confirmPaymentReceived, + confirmPaymentReceivedPassword: confirmPaymentReceivedPassword } From 57338fc896bb9fea13ffd17ec9555b01a6f504e9 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 9 Mar 2019 20:24:41 +0200 Subject: [PATCH 074/193] Fix Missed Delegations Issue - Implement a fix for some delegation transactions which were incorrectly being skipped by old script & go back up to 5 days --- delegations.js | 1612 ++++++++++++++++++++++++------------------------ 1 file changed, 818 insertions(+), 794 deletions(-) diff --git a/delegations.js b/delegations.js index e6d7ee0..7ab2c47 100644 --- a/delegations.js +++ b/delegations.js @@ -1,795 +1,819 @@ -const dsteem = require('dsteem') -//const client = new dsteem.Client('https://steemd.privex.io') -const _ = require('lodash') -const moment = require('moment') -const utils = require('./utils') -const mail = require('./mail') - -const config = utils.getConfig() - -const client = new dsteem.Client(config.active_node) - -const MongoClient = require('mongodb').MongoClient - -const testRun = false; - -let db -let collection -// Database Name -const dbName = config.db_name -const collectionName = 'delegation_transactions' - -let properties -let totalVests -let totalSteem - -let steemPrice = 1; -let sbdPrice = 1; -let newestTxId = -1; - -console.log('--- Reward script initialized ---'); - -var schedule = require('node-schedule') -//console.log('pre-schedule'); -var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ - console.log('--- Start delegators reward ---'); - runRewards(false);//param steemOnlyReward -}); - -//utils.lookupAccountPay(); - -//param steemOnlyReward -runRewards(true); -//runRewards(false); - -function runRewards(steemOnlyReward){ - let mongo_conn = config.mongo_uri - if (config.testing){ - mongo_conn = config.mongo_local - } - // Use connect method to connect to the server - MongoClient.connect(mongo_conn, async function (err, dbClient) { - if (!err) { - console.log('Connected successfully to server: ' + mongo_conn) - - db = dbClient.db(dbName) - // Get the documents collection - collection = db.collection(collectionName) - - //updateUserTokens(); - //return; - - //run for one day - var delegation_days = 1; - startProcess(delegation_days, steemOnlyReward); - - //grab steem prices and proceed checking for beneficiary payouts to AFIT token reward account (full_pay_benef_account) - setInterval(loadSteemPrices,5 * 60 * 1000); - - //claim rewards once per hour - setInterval(claimRewards,60 * 60 * 1000); - - //loadSteemPrices(); - } else { - utils.log(err, 'delegations') - mail.sendPlainMail('Database Error', err, config.report_emails) - .then(function (res, err) { - if (!err) { - console.log(res) - } else { - utils.log(err, 'import') - } - }) - process.exit() - } - }) -} - -//function to grab latest payouts for beneficiaries and reward with AFIT tokens -async function getBenefactorPosts (account, start) { - - //connect to the token_transactions table to start transactions to users - var bulk_transactions = db.collection('token_transactions').initializeUnorderedBulkOp(); - - let totalSBD = 0 - let totalSp = 0 - let limit = 2000; - let txStart = -1; - - start = moment(start).format() - - console.log('start date:'+start) - - //grab current AFIT price in USD - let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next() - console.log('curAfitPrice:'+curAFITPrice.unit_price_usd); - - // Query account history for delegations - properties = await client.database.getDynamicGlobalProperties() - totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) - totalVests = Number(properties.total_vesting_shares.split(' ')[0]) - //console.log(properties); - const transactions = await client.database.call('get_account_history', [account, txStart, limit]) - transactions.reverse() - let foundTx = false; - console.log("newestTxId:"+newestTxId); - let counter = 0; - for (let txs of transactions) { - if (counter == 0){ - counter += 1; - //if this is not our first run, start from where we left - if (newestTxId==txs[0]){ - //no new transactions, bail - console.log('we already went through last transaction'); - break; - }else{ - newestTxId = txs[0]; - console.log('set newestTxId:'+newestTxId); - } - } - let date = moment(txs[1].timestamp).format() - - if (date >= start) { - console.log(txs[0]); - let op = txs[1].op - // Look for beneficiary payments - if (op[0] === 'comment_benefactor_reward') { - foundTx = true; - console.log('---------------------------------------'); - //console.log(op); - let matchingAFIT = 0; - console.log(op[1]); - let rewardedSP = parseFloat(vestsToSteemPower(op[1].vesting_payout).toFixed(3)) - console.log("rewardedSP:"+rewardedSP); - //calculate dollar value - let steemInUSD = rewardedSP * steemPrice; - console.log("steemInUSD:"+steemInUSD); - - //convert to AFIT and add to total - matchingAFIT = steemInUSD / curAFITPrice.unit_price_usd; - console.log("matchingAFIT:"+matchingAFIT); - - let rewardedSTEEM = parseFloat(op[1].steem_payout.split(' ')[0]) - - console.log("rewardedSTEEM:"+rewardedSTEEM); - let steemPureInUSD = rewardedSTEEM * steemPrice; - - let steemPureToAFIT = steemPureInUSD / curAFITPrice.unit_price_usd; - matchingAFIT += steemPureToAFIT; - - let rewardedSBD = parseFloat(op[1].sbd_payout.split(' ')[0]) - - console.log("rewardedSBD:"+rewardedSBD); - - //calculate dollar value - let sbdInUSD = rewardedSBD * sbdPrice; - console.log("sbdInUSD:"+sbdInUSD); - - //convert to AFIT and add to total - let sbdToAFIT = sbdInUSD / curAFITPrice.unit_price_usd; - matchingAFIT += sbdToAFIT; - - //format to 3 decimals - matchingAFIT = parseFloat(matchingAFIT.toFixed(3)); - - console.log("sbdToAFIT:"+sbdToAFIT); - - console.log("Total AFIT:"+matchingAFIT); - - let beneficSwapTansaction = { - user: op[1].author, - reward_activity: 'Full AFIT Payout', - url: op[1].permlink, - token_count: matchingAFIT, - orig_sbd_amount: rewardedSBD, - orig_sp_amount: rewardedSP, - orig_steem_amount: rewardedSTEEM, - date: new Date(date) - } - - //store this as a transaction - bulk_transactions.find( - { - user: beneficSwapTansaction.user, - reward_activity: beneficSwapTansaction.reward_activity, - url: beneficSwapTansaction.url - }).upsert().replaceOne(beneficSwapTansaction); - - } - } else if (date < start){ - break - } - } - //award transaction tokens - if (foundTx){ - bulk_transactions.execute(); - console.log('-- Processed Full AFIT Payouts --') - //once done, update user total token count - updateUserTokens(); - }else{ - console.log('-- No Posts to process --'); - } - -} - -//function to load relevant STEEM and SBD prices, and proceed with AFIT token swap/reward process -function loadSteemPrices() { - - console.log('-- start AFIT token swap process --') - - // Require the "request" library for making HTTP requests - var request = require("request"); - - // Load the price feed data - request.get('https://api.coinmarketcap.com/v1/ticker/steem/', function (e, r, data) { - try { - steemPrice = parseFloat(JSON.parse(data)[0].price_usd); - - console.log("Loaded STEEM price: " + steemPrice); - - // Load the price feed data - request.get('https://api.coinmarketcap.com/v1/ticker/steem-dollars/', function (e, r, data) { - try { - sbdPrice = parseFloat(JSON.parse(data)[0].price_usd); - - console.log("Loaded SBD price: " + sbdPrice); - - let afit_swap_days = 1; - let start = moment().utc().startOf('date').toDate() - let to = moment(start).subtract(afit_swap_days, 'days').toDate() - - //bring the action - getBenefactorPosts(config.full_pay_benef_account, to); - - } catch (err) { - console.log('Error loading SBD price: ' + err); - } - }); - - } catch (err) { - console.log('Error loading STEEM price: ' + err); - } - }); -} - - -async function startProcess (days, steemOnlyReward) { - let end = 0 - // Find last saved delegation transaction - let lastTx = await collection.find().sort({'tx_number': -1}).limit(1).next() - console.log('last recorded delegation transaction'); - console.log(lastTx) - if (lastTx) end = lastTx.tx_number - await updateProperties() - if (!testRun){ - await processDelegations(config.account, -1, end) - } - let start = moment().utc().startOf('date').subtract(days, 'days').toDate() - let txEnd = moment().utc().startOf('date').toDate() - if (!steemOnlyReward){ - console.log('processTokenRewards'); - await processTokenRewards(start, txEnd, days) - //update our user token count post reward - if (!testRun){ - updateUserTokens(); - } - } - var d = new Date(); - var dayId = d.getDay(); - // Check if today is Monday, to calculate steem rewards - if (dayId == 1){ - //console.log('processSteemRewards'); - processSteemRewards(txEnd) - } -} - -async function processTokenRewards (start, end, days) { - if (!start) start = moment().utc().startOf('date').subtract(days, 'days').toDate() - if (!end) end = moment().utc().startOf('date').toDate() - let note = 'Delegation Reward For ' + moment(end).subtract(1, 'days').format('MMMM Do YYYY') - - let acumulatedSteemPower = await getAcumulatedSteemPower(start, end, true); - - //handles maintaining max CAP for payments - let multiplier = 1 - - let currentSteemPower = await getCurrentTotalSP(end); - console.log("currentSteemPower:"+currentSteemPower); - - //check if max CAP is reached, and apply multplier accordingly - if (currentSteemPower > config.weekly_rewards_limit) { - multiplier = config.weekly_rewards_limit / currentSteemPower; - console.log(">>>>went beyond rewards limit. Apply multiplier"); - } - console.log(">>>>multiplier:"+multiplier); - - //load list of alt accounts to reward them instead of actual delegators - let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); - console.log(altAccounts); - - //go through all delegators, and send out AFIT rewards - for (let user of acumulatedSteemPower.users) { - - //skip opt out users from reward - var user_opted_out = false; - for (var n = 0; n < config.exclude_rewards.length; n++) { - if (user.user == config.exclude_rewards[n]){ - console.log('User '+user.user+' opted out from rewards'); - user_opted_out = true; - break; - } - } - if (user_opted_out){ - continue; - } - - let reward_user = user.user; - let reward_activity = 'Delegation'; - - //check if this user has an alt account with delegated rewards enabled - let delegator_entry = _.find(altAccounts, {'delegator': user.user, 'reward_benefit': '1'}); - - //if so reward the alt account instead - if (delegator_entry != null) { - reward_user = delegator_entry.alt_account; - reward_activity += ' On Behalf'; - } - - let reward = { - user: reward_user, - token_count: parseFloat((user.totalSteem * multiplier).toFixed(3)), - reward_activity: reward_activity, - orig_account: user.user, - note: note, - date: end - } - console.log(reward) - //only send out funds if not a test run - if (!testRun){ - upsertRewardTransaction(reward) - } - } -} - -async function processSteemRewards (start) { - if (!start) start = moment().utc().startOf('date').toDate() - // Get active delegations for the week - console.log(config.pay_account) - const to = moment(start).subtract(7, 'days').toDate() - const from = moment(to).subtract(7, 'days').toDate() - - //load list of alt accounts to reward them instead of actual delegators - let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); - console.log('loading alt accounts'); - console.log(altAccounts); - - Promise.all([getAcumulatedSteemPower(from, to, true), getBenefactorRewards(to, start, -1)]).then(values => { - const activeDelegations = values[0].users - console.log('***'); - console.log(values[1]); - const steemRewards = values[1].split(' ')[0] - const sbdRewards = values[1].split(' ')[1] - const totalDelegatedSteem = values[0].totalSteem - const rewardPerSteem = steemRewards / totalDelegatedSteem - const rewardPerSBD = sbdRewards / totalDelegatedSteem - const rewards = _.map(activeDelegations, function (o) { - //skip opt out users from reward - var user_opted_out = false; - for (var n = 0; n < config.exclude_rewards.length; n++) { - if (o.user == config.exclude_rewards[n]){ - console.log('User '+o.user+' opted out from Steem rewards'); - user_opted_out = true; - break; - } - } - - - let reward_user = o.user; - - //check if this user has an alt account with delegated rewards enabled - let delegator_entry = _.find(altAccounts, {'delegator': o.user, 'steem_reward_benefit': '1'}); - - //if so reward the alt account instead - if (delegator_entry != null) { - reward_user = delegator_entry.alt_account; - } - - let reward = {}; - if (!user_opted_out){ - reward = { - user: reward_user, - steem: +(o.totalSteem * rewardPerSteem).toFixed(3), - sbd: +(o.totalSteem * rewardPerSBD).toFixed(3) - } - } - - - - /*let url = 'https://v2.steemconnect.com/sign/transfer?from=[PAY_ACCOUNT]&to=[TO_ACCOUNT]&amount=[AMOUNT]%20STEEM&memo=Delegation%20Rewards' - url = url.replace('[PAY_ACCOUNT]', config.pay_account) - url = url.replace('[TO_ACCOUNT]', reward.user) - url = url.replace('[AMOUNT]', reward.steem) - reward.url = url*/ - return reward - }) - console.log(rewards) - console.log("steem total beneficiary reward:"+steemRewards) - console.log("SBD total beneficiary reward:"+sbdRewards) - const data = { - rewards: rewards, - totalSteem: steemRewards, - totalSBD: sbdRewards, - totalUsers: rewards.length - } - - var fs = require('fs'); - - var fileName = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - fileName = "steemrewards"+fileName+".json"; - console.log("fileName:"+fileName); - fs.writeFile(fileName, JSON.stringify(rewards), function(err) { - if(err) { - return console.log(err); - } - - console.log("The file was saved!"); - }); - /* - const attachment = { - filename: 'rewards.json', - content: JSON.stringify(rewards) - } - mail.sendWithTemplate('Rewards mail', data, config.report_emails, 'rewards', attachment)*/ - }) -} - - - - -function vestsToSteemPower (vests) { - vests = Number(vests.split(' ')[0]) - const steemPower = (totalSteem * (vests / totalVests)) - return steemPower -} - - -async function processDelegations (account, start, end) { - let delegationTransactions = [] - let lastTrans = start - let ended = false - let limit = (start < 0) ? 3000 : Math.min(start, 3000) - console.log('Account: ' + account + ' - Start: ' + start + ' - Limit: ' + limit + ' - Last Txs: ' + end) - try { - // Query account history for delegations - const transactions = await client.database.call('get_account_history', [account, start, limit]) - transactions.reverse() - for (let txs of transactions) { - if (txs[0] === end) { - console.log('--- Found last transaction ---') - ended = true - break - } - let op = txs[1].op - lastTrans = txs[0] - // Look for delegation operations - if (op[0] === 'delegate_vesting_shares' && op[1].delegatee === account) { - // Calculate in steem power - const steemPower = vestsToSteemPower(op[1].vesting_shares) - let data = op[1] - data.steem_power = +steemPower.toFixed(3) - data.tx_number = txs[0] - data.tx_date = new Date(txs[1].timestamp) - delegationTransactions.push(data) - } - } - // Insert new transactions and update active ones - // console.log(delegationTransactions) - if (delegationTransactions.length > 0) { - await collection.insert(delegationTransactions) - await updateActiveDelegations() - } else { - console.log('--- No new delegations ---') - return; - } - // If more pending delegations call process againg with new index - if (start !== limit && !ended){ - return processDelegations(account, lastTrans, end) - } - // console.log(transactions) - return; - } catch (err) { - console.log(err) - // Consider exponential backoff if extreme cases start happening - if (err.type === 'request-timeout' || err.type === 'body-timeout'){ - return processDelegations(account, start, end); - } - } -} - -async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { - if (!totalSBD) totalSBD = 0 - if (!totalSp) totalSp = 0 - let limit = (txStart < 0) ? 10000 : Math.min(txStart, 10000) - start = moment(start).format() - end = moment(end).format() - console.log(start) - console.log(end) - // Query account history for delegations - properties = await client.database.getDynamicGlobalProperties() - totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) - totalVests = Number(properties.total_vesting_shares.split(' ')[0]) - const transactions = await client.database.call('get_account_history', [config.pay_account, txStart, limit]) - transactions.reverse() - for (let txs of transactions) { - let date = moment(txs[1].timestamp).format() - if (date >= start && date <= end) { - let op = txs[1].op - // Look for delegation operations - if (op[0] === 'comment_benefactor_reward') { - console.log(op[1]); - //SP is the sum of conversting vesting payout to SP, and appending any STEEM payouts - let newSp = vestsToSteemPower(op[1].vesting_payout) + parseFloat(op[1].steem_payout.split(' ')[0]) - totalSp = totalSp + newSp - let newSBD = op[1].sbd_payout.split(' ')[0] - totalSBD += parseFloat(newSBD) - } - } else if (date < start) break - } - // Check last tx date to see if pagination is needed - let lastTx = transactions[transactions.length - 1] - let lastDate = moment(lastTx[1].timestamp).format() - // console.log(lastDate) - if (lastDate >= start) return getBenefactorRewards(start, totalSp, lastTx[0]) - - console.log('-- Processed rewards ---') - // console.log(totalSp.toFixed(3)) - return +totalSp.toFixed(3)+' ' +totalSBD.toFixed(3) -} - -async function getActiveDelegations (start, excludeOn) { - start = new Date(start) - if (excludeOn){ - return collection.aggregate( - [ - { $match: { 'tx_date': { '$lte': start } } }, - { $sort: { delegator: 1, tx_date: 1 } }, - { - $group: - { - _id: '$delegator', - steem_power: { $last: '$steem_power' }, - vests: { $last: '$vesting_shares' }, - tx_date: { $last: '$tx_date' } - } - }, - { $project: - { - _id: '$_id', - delegator: '$_id', - steem_power: 1, - tx_date: start - } - }, - { $match: { 'steem_power': { '$gt': 0 }, 'delegator': {$nin: config.exclude_rewards} } }, - { $sort: { tx_date: 1 } } - ] - ).toArray() - }else{ - return collection.aggregate( - [ - { $match: { 'tx_date': { '$lte': start } } }, - { $sort: { delegator: 1, tx_date: 1 } }, - { - $group: - { - _id: '$delegator', - steem_power: { $last: '$steem_power' }, - vests: { $last: '$vesting_shares' }, - tx_date: { $last: '$tx_date' } - } - }, - { $project: - { - _id: '$_id', - delegator: '$_id', - steem_power: 1, - tx_date: start - } - }, - { $match: { 'steem_power': { '$gt': 0 } } }, - { $sort: { tx_date: 1 } } - ] - ).toArray() - } -} - -/* - * function handles grabbing the total current SP value before a specific date - * params: toDate - date before which all current SP is calculated - * returns: total value of current SP count up to passed date - */ -async function getCurrentTotalSP(toDate){ - toDate = moment(toDate).toDate() - - var actDelgCol = 'active_delegations'; - //perform an aggregation based on max date, exluded delegators, and return back sum of SP and delegator count (we only need for now totalSP) - var results = await db.collection(actDelgCol).aggregate([ - { - $match: - { - 'tx_date': {$lt: toDate}, - 'delegator': {$nin: config.exclude_rewards} - } - }, - { - $group: - { - _id: null, - totalSP: { $sum: "$steem_power" }, - totalDelegators: { $sum: 1 } - } - } - ]).toArray(); - //function(err, results) { - //var output = 'tokens distributed:'+results[0].totalSP; - console.log(results); - return results[0].totalSP; - //}); -} - -async function getAcumulatedSteemPower (from, to, excludeOn) { - let result = { - users: [] - } - let totalSteem = 0 - from = moment(from).toDate() - to = moment(to).toDate() - // Get active delegations for the week - let activeDelegations = await getActiveDelegations(from, excludeOn) - // Get transactions of the processed week - let weekTxs - if (excludeOn){ - console.log('excluding users'); - weekTxs = await db.collection('delegation_transactions').find( - {'tx_date': {$gt: from, $lt: to}, - 'delegator': {$nin: config.exclude_rewards}}) - .sort({tx_date: 1}).toArray() - }else{ - console.log('no exclude'); - weekTxs = await db.collection('delegation_transactions').find( - {'tx_date': {$gt: from, $lt: to}}) - .sort({tx_date: 1}).toArray() - } - let allTxs = activeDelegations.concat(weekTxs) - let groupedTxs = _.groupBy(allTxs, 'delegator') - for (let index in groupedTxs) { - let totalReward = 0 - for (let i = 0; i < groupedTxs[index].length; i++) { - let txs = groupedTxs[index][i] - let endTxs - // If not last transaction calculate up to next one - if (i !== groupedTxs[index].length - 1) endTxs = new Date(groupedTxs[index][i + 1].tx_date) - else endTxs = to - var activeHours = Math.abs(txs.tx_date - endTxs) / 36e5 - let newReward = activeHours * (txs.steem_power / 24) - totalReward = totalReward + newReward - } - totalSteem = totalSteem + totalReward - let user = { - user: index, - totalSteem: totalReward - } - result.users.push(user) - } - result.totalSteem = totalSteem - return result -} - -async function updateActiveDelegations () { - console.log('--- Updating active delegations ---') - let query = collection.aggregate( - [ - { $sort: { delegator: 1, tx_date: 1 } }, - { - $group: - { - _id: '$delegator', - steem_power: { $last: '$steem_power' }, - vests: { $last: '$vesting_shares' }, - tx_date: { $last: '$tx_date' } - } - }, - { $match: { 'steem_power': { '$gt': 0 } } } - ] - ) - let activeDelegations = await query.toArray() - await db.collection('active_delegations').drop() - await db.collection('active_delegations').insert(activeDelegations) - console.log('done updating delegations'); - return ; -} - -function upsertRewardTransaction (reward) { - return db.collection('token_transactions').update( - { user: reward.user, date: reward.date, reward_activity: reward.reward_activity }, - reward, - { upsert: true } - ) -} - - -async function updateProperties () { - // Set STEEM global properties - properties = await client.database.getDynamicGlobalProperties() - totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) - totalVests = Number(properties.total_vesting_shares.split(' ')[0]) -} - -//function handles updating current user token count -async function updateUserTokens() { - console.log('---- Updating User Tokens ----'); - - try{ - //group all token transactions per user, and sum them to generate new total count - let query = await db.collection('token_transactions').aggregate([ - { $group: { _id: "$user", tokens: { $sum: "$token_count" } } }, - { $sort: { tokens: -1 } }, - { $project: { - _id: "$_id", - user: "$_id", - tokens: "$tokens", - } - } - ]) - - let user_tokens = await query.toArray(); - //remove old token count per user - await db.collection('user_tokens').remove({}); - //insert new count per user - await db.collection('user_tokens').insert(user_tokens); - console.log('---- Updating User Tokens Complete ----'); - }catch(err){ - console.log('>>save data error:'+err.message); - } -} -//function handles fetching account details for later use when claiming rewards -async function grabAccountDetails(){ - console.log('grabbing fund account details'); - let account = await client.database.call('get_accounts', [[config.full_pay_benef_account]]); - console.log(account); - return account[0]; -} -//function handles claiming pending account rewards -async function claimRewards(){ - //sign key properly to function with dsteem requirement - let privateKey = dsteem.PrivateKey.fromString( - config.full_pay_posting_key - ); - //fetch account details first to use correct values for claim - let funds_account = await grabAccountDetails(); - console.log(funds_account.reward_steem_balance); - console.log(funds_account.reward_sbd_balance); - console.log(funds_account.reward_vesting_balance); - //if we have any value to claim, proceed - if (parseFloat(funds_account.reward_steem_balance) > 0 || parseFloat(funds_account.reward_sbd_balance) > 0 || parseFloat(funds_account.reward_vesting_balance) > 0) { - const op = [ - 'claim_reward_balance', - { - account: config.full_pay_benef_account, - reward_steem: funds_account.reward_steem_balance.split(' ')[0] + ' STEEM', - reward_sbd: funds_account.reward_sbd_balance.split(' ')[0] + ' SBD', - reward_vests: funds_account.reward_vesting_balance.split(' ')[0] + ' VESTS', - }, - ]; - client.broadcast.sendOperations([op], privateKey).then( - function(result) { - console.log(result); - }, - function(error) { - console.log(error); - } - ) - }else{ - console.log('no rewards to claim for now'); - } +const dsteem = require('dsteem') +//const client = new dsteem.Client('https://steemd.privex.io') +const _ = require('lodash') +const moment = require('moment') +const utils = require('./utils') +const mail = require('./mail') + +const config = utils.getConfig() + +const client = new dsteem.Client(config.active_node) + +const MongoClient = require('mongodb').MongoClient + +const testRun = false; + +let db +let collection +let bulk_delegation_entries + +// Database Name +const dbName = config.db_name +const collectionName = 'delegation_transactions' + +let properties +let totalVests +let totalSteem + +let steemPrice = 1; +let sbdPrice = 1; +let newestTxId = -1; + +console.log('--- Reward script initialized ---'); + +var schedule = require('node-schedule') +//console.log('pre-schedule'); +var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ + console.log('--- Start delegators reward ---'); + runRewards(false);//param steemOnlyReward +}); + +//utils.lookupAccountPay(); + +//param steemOnlyReward +runRewards(true); +//runRewards(false); + +function runRewards(steemOnlyReward){ + let mongo_conn = config.mongo_uri + if (config.testing){ + mongo_conn = config.mongo_local + } + // Use connect method to connect to the server + MongoClient.connect(mongo_conn, async function (err, dbClient) { + if (!err) { + console.log('Connected successfully to server: ' + mongo_conn) + + db = dbClient.db(dbName) + // Get the documents collection + collection = db.collection(collectionName) + + bulk_delegation_entries = db.collection(collectionName).initializeUnorderedBulkOp(); + + //updateUserTokens(); + //return; + + //run for one day + var delegation_days = 1; + startProcess(delegation_days, steemOnlyReward); + + //grab steem prices and proceed checking for beneficiary payouts to AFIT token reward account (full_pay_benef_account) + setInterval(loadSteemPrices,5 * 60 * 1000); + + //claim rewards once per hour + setInterval(claimRewards,60 * 60 * 1000); + + //loadSteemPrices(); + } else { + utils.log(err, 'delegations') + mail.sendPlainMail('Database Error', err, config.report_emails) + .then(function (res, err) { + if (!err) { + console.log(res) + } else { + utils.log(err, 'import') + } + }) + process.exit() + } + }) +} + +//function to grab latest payouts for beneficiaries and reward with AFIT tokens +async function getBenefactorPosts (account, start) { + + //connect to the token_transactions table to start transactions to users + var bulk_transactions = db.collection('token_transactions').initializeUnorderedBulkOp(); + + let totalSBD = 0 + let totalSp = 0 + let limit = 2000; + let txStart = -1; + + start = moment(start).format() + + console.log('start date:'+start) + + //grab current AFIT price in USD + let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next() + console.log('curAfitPrice:'+curAFITPrice.unit_price_usd); + + // Query account history for delegations + properties = await client.database.getDynamicGlobalProperties() + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) + totalVests = Number(properties.total_vesting_shares.split(' ')[0]) + //console.log(properties); + const transactions = await client.database.call('get_account_history', [account, txStart, limit]) + transactions.reverse() + let foundTx = false; + console.log("newestTxId:"+newestTxId); + let counter = 0; + for (let txs of transactions) { + if (counter == 0){ + counter += 1; + //if this is not our first run, start from where we left + if (newestTxId==txs[0]){ + //no new transactions, bail + console.log('we already went through last transaction'); + break; + }else{ + newestTxId = txs[0]; + console.log('set newestTxId:'+newestTxId); + } + } + let date = moment(txs[1].timestamp).format() + + if (date >= start) { + console.log(txs[0]); + let op = txs[1].op + // Look for beneficiary payments + if (op[0] === 'comment_benefactor_reward') { + foundTx = true; + console.log('---------------------------------------'); + //console.log(op); + let matchingAFIT = 0; + console.log(op[1]); + let rewardedSP = parseFloat(vestsToSteemPower(op[1].vesting_payout).toFixed(3)) + console.log("rewardedSP:"+rewardedSP); + //calculate dollar value + let steemInUSD = rewardedSP * steemPrice; + console.log("steemInUSD:"+steemInUSD); + + //convert to AFIT and add to total + matchingAFIT = steemInUSD / curAFITPrice.unit_price_usd; + console.log("matchingAFIT:"+matchingAFIT); + + let rewardedSTEEM = parseFloat(op[1].steem_payout.split(' ')[0]) + + console.log("rewardedSTEEM:"+rewardedSTEEM); + let steemPureInUSD = rewardedSTEEM * steemPrice; + + let steemPureToAFIT = steemPureInUSD / curAFITPrice.unit_price_usd; + matchingAFIT += steemPureToAFIT; + + let rewardedSBD = parseFloat(op[1].sbd_payout.split(' ')[0]) + + console.log("rewardedSBD:"+rewardedSBD); + + //calculate dollar value + let sbdInUSD = rewardedSBD * sbdPrice; + console.log("sbdInUSD:"+sbdInUSD); + + //convert to AFIT and add to total + let sbdToAFIT = sbdInUSD / curAFITPrice.unit_price_usd; + matchingAFIT += sbdToAFIT; + + //format to 3 decimals + matchingAFIT = parseFloat(matchingAFIT.toFixed(3)); + + console.log("sbdToAFIT:"+sbdToAFIT); + + console.log("Total AFIT:"+matchingAFIT); + + let beneficSwapTansaction = { + user: op[1].author, + reward_activity: 'Full AFIT Payout', + url: op[1].permlink, + token_count: matchingAFIT, + orig_sbd_amount: rewardedSBD, + orig_sp_amount: rewardedSP, + orig_steem_amount: rewardedSTEEM, + date: new Date(date) + } + + //store this as a transaction + bulk_transactions.find( + { + user: beneficSwapTansaction.user, + reward_activity: beneficSwapTansaction.reward_activity, + url: beneficSwapTansaction.url + }).upsert().replaceOne(beneficSwapTansaction); + + } + } else if (date < start){ + break + } + } + //award transaction tokens + if (foundTx){ + bulk_transactions.execute(); + console.log('-- Processed Full AFIT Payouts --') + //once done, update user total token count + updateUserTokens(); + }else{ + console.log('-- No Posts to process --'); + } + +} + +//function to load relevant STEEM and SBD prices, and proceed with AFIT token swap/reward process +function loadSteemPrices() { + + console.log('-- start AFIT token swap process --') + + // Require the "request" library for making HTTP requests + var request = require("request"); + + // Load the price feed data + request.get('https://api.coinmarketcap.com/v1/ticker/steem/', function (e, r, data) { + try { + steemPrice = parseFloat(JSON.parse(data)[0].price_usd); + + console.log("Loaded STEEM price: " + steemPrice); + + // Load the price feed data + request.get('https://api.coinmarketcap.com/v1/ticker/steem-dollars/', function (e, r, data) { + try { + sbdPrice = parseFloat(JSON.parse(data)[0].price_usd); + + console.log("Loaded SBD price: " + sbdPrice); + + let afit_swap_days = 1; + let start = moment().utc().startOf('date').toDate() + let to = moment(start).subtract(afit_swap_days, 'days').toDate() + + //bring the action + getBenefactorPosts(config.full_pay_benef_account, to); + + } catch (err) { + console.log('Error loading SBD price: ' + err); + } + }); + + } catch (err) { + console.log('Error loading STEEM price: ' + err); + } + }); +} + + +async function startProcess (days, steemOnlyReward) { + let end = 0 + // Find last saved delegation transaction + //let lastTx = await collection.find().sort({'tx_number': -1}).limit(1).next() + console.log('last recorded delegation transaction'); + //console.log(lastTx) + //if (lastTx) end = lastTx.tx_number + await updateProperties() + if (!testRun){ + await processDelegations(config.account, -1, end) + } + let start = moment().utc().startOf('date').subtract(days, 'days').toDate() + let txEnd = moment().utc().startOf('date').toDate() + if (!steemOnlyReward){ + console.log('processTokenRewards'); + await processTokenRewards(start, txEnd, days) + //update our user token count post reward + if (!testRun){ + updateUserTokens(); + } + } + var d = new Date(); + var dayId = d.getDay(); + // Check if today is Monday, to calculate steem rewards + if (dayId == 1){ + //console.log('processSteemRewards'); + processSteemRewards(txEnd) + } +} + +async function processTokenRewards (start, end, days) { + if (!start) start = moment().utc().startOf('date').subtract(days, 'days').toDate() + if (!end) end = moment().utc().startOf('date').toDate() + let note = 'Delegation Reward For ' + moment(end).subtract(1, 'days').format('MMMM Do YYYY') + + let acumulatedSteemPower = await getAcumulatedSteemPower(start, end, true); + + //handles maintaining max CAP for payments + let multiplier = 1 + + let currentSteemPower = await getCurrentTotalSP(end); + console.log("currentSteemPower:"+currentSteemPower); + + //check if max CAP is reached, and apply multplier accordingly + if (currentSteemPower > config.weekly_rewards_limit) { + multiplier = config.weekly_rewards_limit / currentSteemPower; + console.log(">>>>went beyond rewards limit. Apply multiplier"); + } + console.log(">>>>multiplier:"+multiplier); + + //load list of alt accounts to reward them instead of actual delegators + let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); + console.log(altAccounts); + + //go through all delegators, and send out AFIT rewards + for (let user of acumulatedSteemPower.users) { + + //skip opt out users from reward + var user_opted_out = false; + for (var n = 0; n < config.exclude_rewards.length; n++) { + if (user.user == config.exclude_rewards[n]){ + console.log('User '+user.user+' opted out from rewards'); + user_opted_out = true; + break; + } + } + if (user_opted_out){ + continue; + } + + let reward_user = user.user; + let reward_activity = 'Delegation'; + + //check if this user has an alt account with delegated rewards enabled + let delegator_entry = _.find(altAccounts, {'delegator': user.user, 'reward_benefit': '1'}); + + //if so reward the alt account instead + if (delegator_entry != null) { + reward_user = delegator_entry.alt_account; + reward_activity += ' On Behalf'; + } + + let reward = { + user: reward_user, + token_count: parseFloat((user.totalSteem * multiplier).toFixed(3)), + reward_activity: reward_activity, + orig_account: user.user, + note: note, + date: end + } + console.log(reward) + //only send out funds if not a test run + if (!testRun){ + upsertRewardTransaction(reward) + } + } +} + +async function processSteemRewards (start) { + if (!start) start = moment().utc().startOf('date').toDate() + // Get active delegations for the week + console.log(config.pay_account) + const to = moment(start).subtract(7, 'days').toDate() + const from = moment(to).subtract(7, 'days').toDate() + + //load list of alt accounts to reward them instead of actual delegators + let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); + console.log('loading alt accounts'); + console.log(altAccounts); + + Promise.all([getAcumulatedSteemPower(from, to, true), getBenefactorRewards(to, start, -1)]).then(values => { + const activeDelegations = values[0].users + console.log('***'); + console.log(values[1]); + const steemRewards = values[1].split(' ')[0] + const sbdRewards = values[1].split(' ')[1] + const totalDelegatedSteem = values[0].totalSteem + const rewardPerSteem = steemRewards / totalDelegatedSteem + const rewardPerSBD = sbdRewards / totalDelegatedSteem + const rewards = _.map(activeDelegations, function (o) { + //skip opt out users from reward + var user_opted_out = false; + for (var n = 0; n < config.exclude_rewards.length; n++) { + if (o.user == config.exclude_rewards[n]){ + console.log('User '+o.user+' opted out from Steem rewards'); + user_opted_out = true; + break; + } + } + + + let reward_user = o.user; + + //check if this user has an alt account with delegated rewards enabled + let delegator_entry = _.find(altAccounts, {'delegator': o.user, 'steem_reward_benefit': '1'}); + + //if so reward the alt account instead + if (delegator_entry != null) { + reward_user = delegator_entry.alt_account; + } + + let reward = {}; + if (!user_opted_out){ + reward = { + user: reward_user, + steem: +(o.totalSteem * rewardPerSteem).toFixed(3), + sbd: +(o.totalSteem * rewardPerSBD).toFixed(3) + } + } + + + + /*let url = 'https://v2.steemconnect.com/sign/transfer?from=[PAY_ACCOUNT]&to=[TO_ACCOUNT]&amount=[AMOUNT]%20STEEM&memo=Delegation%20Rewards' + url = url.replace('[PAY_ACCOUNT]', config.pay_account) + url = url.replace('[TO_ACCOUNT]', reward.user) + url = url.replace('[AMOUNT]', reward.steem) + reward.url = url*/ + return reward + }) + console.log(rewards) + console.log("steem total beneficiary reward:"+steemRewards) + console.log("SBD total beneficiary reward:"+sbdRewards) + const data = { + rewards: rewards, + totalSteem: steemRewards, + totalSBD: sbdRewards, + totalUsers: rewards.length + } + + var fs = require('fs'); + + var fileName = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + fileName = "steemrewards"+fileName+".json"; + console.log("fileName:"+fileName); + fs.writeFile(fileName, JSON.stringify(rewards), function(err) { + if(err) { + return console.log(err); + } + + console.log("The file was saved!"); + }); + /* + const attachment = { + filename: 'rewards.json', + content: JSON.stringify(rewards) + } + mail.sendWithTemplate('Rewards mail', data, config.report_emails, 'rewards', attachment)*/ + }) +} + + + + +function vestsToSteemPower (vests) { + vests = Number(vests.split(' ')[0]) + const steemPower = (totalSteem * (vests / totalVests)) + return steemPower +} + + +async function processDelegations (account, start, end) { + let delegationTransactions = [] + let lastTrans = start + let ended = false + let limit = (start < 0) ? 3000 : Math.min(start, 3000) + console.log('Account: ' + account + ' - Start: ' + start + ' - Limit: ' + limit + ' - Last Txs: ' + end) + try { + // Query account history for delegations + const transactions = await client.database.call('get_account_history', [account, start, limit]) + transactions.reverse() + for (let txs of transactions) { + //let's only fetch a max of 5 days ago delegation transactions + let tx_date = moment(txs[1].timestamp).format() + + //today + let start = moment().utc().startOf('date').toDate() + + let to = moment(start).subtract(5, 'days').toDate() + let end = moment(to).format() + + if (txs[0] === end || tx_date < end) { + console.log('--- Found last transaction ---') + ended = true + break + } + let op = txs[1].op + lastTrans = txs[0] + // Look for delegation operations + if (op[0] === 'delegate_vesting_shares' && op[1].delegatee === account) { + //console.log(txs); + // Calculate in steem power + const steemPower = vestsToSteemPower(op[1].vesting_shares) + let data = op[1] + data.steem_power = +steemPower.toFixed(3) + data.tx_number = txs[0] + data.tx_date = new Date(txs[1].timestamp) + delegationTransactions.push(data) + + bulk_delegation_entries.find( + { + delegator: data.delegator, + vesting_shares: data.vesting_shares, + }).upsert().replaceOne(data); + } + } + // Insert new transactions and update active ones + if (delegationTransactions.length > 0) { + try{ + await bulk_delegation_entries.execute(); + }catch(bulkerr){ + utils.log(bulkerr); + } + //await collection.insert(delegationTransactions) + await updateActiveDelegations() + } else { + console.log('--- No new delegations ---') + return; + } + // If more pending delegations call process againg with new index + if (start !== limit && !ended){ + return processDelegations(account, lastTrans, end) + } + // console.log(transactions) + return; + } catch (err) { + console.log(err) + // Consider exponential backoff if extreme cases start happening + if (err.type === 'request-timeout' || err.type === 'body-timeout'){ + return processDelegations(account, start, end); + } + } +} + +async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { + if (!totalSBD) totalSBD = 0 + if (!totalSp) totalSp = 0 + let limit = (txStart < 0) ? 10000 : Math.min(txStart, 10000) + start = moment(start).format() + end = moment(end).format() + console.log(start) + console.log(end) + // Query account history for delegations + properties = await client.database.getDynamicGlobalProperties() + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) + totalVests = Number(properties.total_vesting_shares.split(' ')[0]) + const transactions = await client.database.call('get_account_history', [config.pay_account, txStart, limit]) + transactions.reverse() + for (let txs of transactions) { + let date = moment(txs[1].timestamp).format() + if (date >= start && date <= end) { + let op = txs[1].op + // Look for delegation operations + if (op[0] === 'comment_benefactor_reward') { + console.log(op[1]); + //SP is the sum of conversting vesting payout to SP, and appending any STEEM payouts + let newSp = vestsToSteemPower(op[1].vesting_payout) + parseFloat(op[1].steem_payout.split(' ')[0]) + totalSp = totalSp + newSp + let newSBD = op[1].sbd_payout.split(' ')[0] + totalSBD += parseFloat(newSBD) + } + } else if (date < start) break + } + // Check last tx date to see if pagination is needed + let lastTx = transactions[transactions.length - 1] + let lastDate = moment(lastTx[1].timestamp).format() + // console.log(lastDate) + if (lastDate >= start) return getBenefactorRewards(start, totalSp, lastTx[0]) + + console.log('-- Processed rewards ---') + // console.log(totalSp.toFixed(3)) + return +totalSp.toFixed(3)+' ' +totalSBD.toFixed(3) +} + +async function getActiveDelegations (start, excludeOn) { + start = new Date(start) + if (excludeOn){ + return collection.aggregate( + [ + { $match: { 'tx_date': { '$lte': start } } }, + { $sort: { delegator: 1, tx_date: 1 } }, + { + $group: + { + _id: '$delegator', + steem_power: { $last: '$steem_power' }, + vests: { $last: '$vesting_shares' }, + tx_date: { $last: '$tx_date' } + } + }, + { $project: + { + _id: '$_id', + delegator: '$_id', + steem_power: 1, + tx_date: start + } + }, + { $match: { 'steem_power': { '$gt': 0 }, 'delegator': {$nin: config.exclude_rewards} } }, + { $sort: { tx_date: 1 } } + ] + ).toArray() + }else{ + return collection.aggregate( + [ + { $match: { 'tx_date': { '$lte': start } } }, + { $sort: { delegator: 1, tx_date: 1 } }, + { + $group: + { + _id: '$delegator', + steem_power: { $last: '$steem_power' }, + vests: { $last: '$vesting_shares' }, + tx_date: { $last: '$tx_date' } + } + }, + { $project: + { + _id: '$_id', + delegator: '$_id', + steem_power: 1, + tx_date: start + } + }, + { $match: { 'steem_power': { '$gt': 0 } } }, + { $sort: { tx_date: 1 } } + ] + ).toArray() + } +} + +/* + * function handles grabbing the total current SP value before a specific date + * params: toDate - date before which all current SP is calculated + * returns: total value of current SP count up to passed date + */ +async function getCurrentTotalSP(toDate){ + toDate = moment(toDate).toDate() + + var actDelgCol = 'active_delegations'; + //perform an aggregation based on max date, exluded delegators, and return back sum of SP and delegator count (we only need for now totalSP) + var results = await db.collection(actDelgCol).aggregate([ + { + $match: + { + 'tx_date': {$lt: toDate}, + 'delegator': {$nin: config.exclude_rewards} + } + }, + { + $group: + { + _id: null, + totalSP: { $sum: "$steem_power" }, + totalDelegators: { $sum: 1 } + } + } + ]).toArray(); + //function(err, results) { + //var output = 'tokens distributed:'+results[0].totalSP; + console.log(results); + return results[0].totalSP; + //}); +} + +async function getAcumulatedSteemPower (from, to, excludeOn) { + let result = { + users: [] + } + let totalSteem = 0 + from = moment(from).toDate() + to = moment(to).toDate() + // Get active delegations for the week + let activeDelegations = await getActiveDelegations(from, excludeOn) + // Get transactions of the processed week + let weekTxs + if (excludeOn){ + console.log('excluding users'); + weekTxs = await db.collection('delegation_transactions').find( + {'tx_date': {$gt: from, $lt: to}, + 'delegator': {$nin: config.exclude_rewards}}) + .sort({tx_date: 1}).toArray() + }else{ + console.log('no exclude'); + weekTxs = await db.collection('delegation_transactions').find( + {'tx_date': {$gt: from, $lt: to}}) + .sort({tx_date: 1}).toArray() + } + let allTxs = activeDelegations.concat(weekTxs) + let groupedTxs = _.groupBy(allTxs, 'delegator') + for (let index in groupedTxs) { + let totalReward = 0 + for (let i = 0; i < groupedTxs[index].length; i++) { + let txs = groupedTxs[index][i] + let endTxs + // If not last transaction calculate up to next one + if (i !== groupedTxs[index].length - 1) endTxs = new Date(groupedTxs[index][i + 1].tx_date) + else endTxs = to + var activeHours = Math.abs(txs.tx_date - endTxs) / 36e5 + let newReward = activeHours * (txs.steem_power / 24) + totalReward = totalReward + newReward + } + totalSteem = totalSteem + totalReward + let user = { + user: index, + totalSteem: totalReward + } + result.users.push(user) + } + result.totalSteem = totalSteem + return result +} + +async function updateActiveDelegations () { + console.log('--- Updating active delegations ---') + let query = collection.aggregate( + [ + { $sort: { delegator: 1, tx_date: 1 } }, + { + $group: + { + _id: '$delegator', + steem_power: { $last: '$steem_power' }, + vests: { $last: '$vesting_shares' }, + tx_date: { $last: '$tx_date' } + } + }, + { $match: { 'steem_power': { '$gt': 0 } } } + ] + ) + let activeDelegations = await query.toArray() + await db.collection('active_delegations').drop() + await db.collection('active_delegations').insert(activeDelegations) + console.log('done updating delegations'); + return ; +} + +function upsertRewardTransaction (reward) { + return db.collection('token_transactions').update( + { user: reward.user, date: reward.date, reward_activity: reward.reward_activity }, + reward, + { upsert: true } + ) +} + + +async function updateProperties () { + // Set STEEM global properties + properties = await client.database.getDynamicGlobalProperties() + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) + totalVests = Number(properties.total_vesting_shares.split(' ')[0]) +} + +//function handles updating current user token count +async function updateUserTokens() { + console.log('---- Updating User Tokens ----'); + + try{ + //group all token transactions per user, and sum them to generate new total count + let query = await db.collection('token_transactions').aggregate([ + { $group: { _id: "$user", tokens: { $sum: "$token_count" } } }, + { $sort: { tokens: -1 } }, + { $project: { + _id: "$_id", + user: "$_id", + tokens: "$tokens", + } + } + ]) + + let user_tokens = await query.toArray(); + //remove old token count per user + await db.collection('user_tokens').remove({}); + //insert new count per user + await db.collection('user_tokens').insert(user_tokens); + console.log('---- Updating User Tokens Complete ----'); + }catch(err){ + console.log('>>save data error:'+err.message); + } +} +//function handles fetching account details for later use when claiming rewards +async function grabAccountDetails(){ + console.log('grabbing fund account details'); + let account = await client.database.call('get_accounts', [[config.full_pay_benef_account]]); + console.log(account); + return account[0]; +} +//function handles claiming pending account rewards +async function claimRewards(){ + //sign key properly to function with dsteem requirement + let privateKey = dsteem.PrivateKey.fromString( + config.full_pay_posting_key + ); + //fetch account details first to use correct values for claim + let funds_account = await grabAccountDetails(); + console.log(funds_account.reward_steem_balance); + console.log(funds_account.reward_sbd_balance); + console.log(funds_account.reward_vesting_balance); + //if we have any value to claim, proceed + if (parseFloat(funds_account.reward_steem_balance) > 0 || parseFloat(funds_account.reward_sbd_balance) > 0 || parseFloat(funds_account.reward_vesting_balance) > 0) { + const op = [ + 'claim_reward_balance', + { + account: config.full_pay_benef_account, + reward_steem: funds_account.reward_steem_balance.split(' ')[0] + ' STEEM', + reward_sbd: funds_account.reward_sbd_balance.split(' ')[0] + ' SBD', + reward_vests: funds_account.reward_vesting_balance.split(' ')[0] + ' VESTS', + }, + ]; + client.broadcast.sendOperations([op], privateKey).then( + function(result) { + console.log(result); + }, + function(error) { + console.log(error); + } + ) + }else{ + console.log('no rewards to claim for now'); + } } \ No newline at end of file From 98fc9176307956e2292c8827dadd9dad32ed8f05 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 9 Mar 2019 20:28:29 +0200 Subject: [PATCH 075/193] New Endpoints in Support of Token Exchange - Implement new endpoints that allow confirming payment receipt, checking if user has pending exchanges in place, list of pending exchanges, list of processed token exchanges, unverified account list, full list of funds password accounts - Implement new endpoint for rewarding users for posting comments via actifit.io - Implement new endpoint for grabbing total posts submitted for Actifit on a single given date --- app.js | 308 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 303 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 117f0f0..9714891 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,7 @@ var exphbs = require('express-handlebars'); const MongoClient = require('mongodb').MongoClient; var utils = require('./utils'); const moment = require('moment') +var crypto = require('crypto'); var appPort = process.env.PORT || 3120; @@ -66,8 +67,8 @@ app.get('/', function (req, res) { /* function handles calculating and returning user token count */ -grabUserTokensFunc = async function (req, res){ - let user = await collection.findOne({_id: req.params.user}, {fields : { _id:0} }); +grabUserTokensFunc = async function (username){ + let user = await collection.findOne({_id: username}); console.log(user); //fixing token amount display for 3 digits if (typeof user!= "undefined" && user!=null){ @@ -80,7 +81,7 @@ grabUserTokensFunc = async function (req, res){ /* end point for user total token count display */ app.get('/user/:user', async function (req, res) { - let user = await grabUserTokensFunc(req,res); + let user = await grabUserTokensFunc(req.params.user); res.send(user); }); @@ -546,7 +547,7 @@ app.get('/getRank/:user', async function (req, res) { user_rank += delegation_score; //grab user token count - var userTokens = await grabUserTokensFunc(req,res); + var userTokens = await grabUserTokensFunc(req.params.user); //console.log(userTokens.tokens); var afit_tokens_score = 0; @@ -941,7 +942,7 @@ app.get('/confirmPayment', async function(req,res){ //keeping request alive to avoid timeouts let intID = setInterval(function(){ res.write(' '); - }, 3000); + }, 6000); try{ //first step is to ensure memo has not been tampered with, nor has it been claimed before //to do that, let's try to find if any signup has been done using this memo @@ -1014,6 +1015,7 @@ app.get('/appendVerifiedPost', async function(req,res){ author: req.query.author, permlink: req.query.permlink, json_metadata: JSON.parse(req.query.json_metadata), + date: new Date(), }; try{ let transaction = await db.collection('verified_posts').insert(verified_post); @@ -1044,6 +1046,258 @@ app.get('/fetchVerifiedPost', async function(req,res){ } }); + +/* end point for checking if user has funds pass */ +app.get('/userHasFundsPassSet/:user', async function (req, res) { + let query = {user: req.params.user}; + console.log(query); + let entryFound = await db.collection('account_funds_pass').findOne(query, {fields : { _id:0} }); + console.log(entryFound); + if (entryFound != null){ + res.send({'hasFundsPass': true, 'passVerified': entryFound.passVerified, 'date': entryFound.date}); + }else{ + res.send({'hasFundsPass': false}); + } +}); + +/* end point for setting a user's funds pass */ +app.get('/setUserFundsPass/:user/:pass', async function (req, res) { + let query = {user: req.params.user}; + console.log(query); + let entryFound = await db.collection('account_funds_pass').findOne(query, {fields : { _id:0} }); + + let proceed = true; + //password can be set/replaced only if none exists, or its not verified already and has not been 10 mins since setting first attempt + if (entryFound == null || + (!entryFound.passVerified)){ + if (entryFound != null){ + + //checking last entry date + var now = moment(new Date()); //todays date + var end = moment(entryFound.date); // last update date + var duration = moment.duration(now.diff(end)); + var mins = duration.asMinutes(); + console.log(mins); + if (mins < 10){ + res.send({'error': 'You can only update your funds pass once every 10 minutes'}); + proceed = false; + } + } + if (proceed){ + + //create encrypted version of the password + var cipher = crypto.createCipher(config.funds_encr_mode, config.funds_encr_key); + let encr_pass = cipher.update(req.params.pass, 'utf8', 'hex'); + encr_pass += cipher.final('hex'); + + //store pass with unverified status + let new_pass_entry = {user: req.params.user, pass: encr_pass, passVerified: false, date: new Date()}; + try{ + let transaction = await db.collection('account_funds_pass') + .replaceOne(query, new_pass_entry, { upsert: true }); + res.send({'status': 'Success'}); + }catch(e){ + console.log(e); + res.send({'error': 'Error setting your funds password. Please contact us on discord if you wish to do so.'}); + } + } + }else{ + res.send({'error': 'You cannot change your verified funds password. Please contact us on discord if you wish to do so.'}); + } + +}); + + + +//function handles the process of confirming password verification receipt, and sets proper password status accordingly +app.get('/confirmPaymentPasswordVerify', async function(req,res){ + let paymentReceivedTx = ''; + let statusUpdated = false; + //keeping request alive to avoid timeouts + let intID = setInterval(function(){ + res.write(' '); + },8000); + try{ + paymentReceivedTx = await utils.confirmPaymentReceivedPassword(req, config.signup_account); + console.log('>>>> got TX '+paymentReceivedTx); + if (paymentReceivedTx != ''){ + try{ + //we found the transfer, now let's update the status properly + let query = {user: req.query.from}; + console.log(query); + let entryFound = await db.collection('account_funds_pass').findOne(query); + console.log(entryFound); + if (entryFound != null && + (!entryFound.passVerified)){ + try{ + //we need to set this transaction as processed via upvote + entryFound.passVerified = true; + let transaction = await db.collection('account_funds_pass').save(entryFound); + statusUpdated = true; + console.log('saved'); + }catch(e){ + console.log(e); + } + } + }catch(e){ + console.log(e); + } + } + }catch(err){ + console.log(err); + } + //we're done, let's clear our running interval + clearInterval(intID); + //res.send({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated}); + res.write(JSON.stringify({'paymentReceivedTx': paymentReceivedTx, 'statusUpdated': statusUpdated})); + res.end(); +}); + + +/* end point finding whether user has a pending AFIT token swap */ +app.get('/userHasPendingTokenSwap/:user', async function(req, res){ + let user_pending_swap = await db.collection('exchange_afit_steem').findOne({user: req.params.user,upvote_processed: {$in: [null, false, 'false']}},{fields : { _id:0} }); + res.send({user_pending_swap: user_pending_swap}); +}); + +/* end point for getting number of AFIT -> STEEM upvotes pending exchanges */ +app.get('/getPendingTokenSwapTransCount/', async function(req, res){ + let tokenSwapTrans = await db.collection('exchange_afit_steem').find({upvote_processed: {$in: [null, false, 'false']}}).sort({'date': 1}).toArray(); + res.send({pendingSwap: tokenSwapTrans.length}); +}); + +/* end point for getting exchanges pending upvotes */ +app.get('/getPendingTokenSwapTrans/', async function(req, res){ + let tokenSwapTrans = await db.collection('exchange_afit_steem').find({upvote_processed: {$in: [null, false, 'false']}}).sort({'date': 1}).toArray(); + //generate total AFIT value as well + let afit_count = 0; + for (let i=0;i Date: Sat, 9 Mar 2019 20:31:25 +0200 Subject: [PATCH 076/193] Implement core functionality for AFIT exchange - Implement core functionality enabling the voting/rewarding script to upvote based on pending list of exchange transactions --- comment.md | 34 ++-- curation-bot.js | 484 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 402 insertions(+), 116 deletions(-) diff --git a/comment.md b/comment.md index cded4e0..cbf740e 100644 --- a/comment.md +++ b/comment.md @@ -1,18 +1,18 @@ -Congrats on providing **Proof of Activity** via your Actifit report! -{lucky_reward} -You have accordingly been rewarded {token_count} AFIT tokens for your effort in reaching {step_count} activity, as well as your user rank and report quality! -You also received an {weight}% upvote via @actifit account. -Actifit rewards and upvotes are based on your: -- User rank: which depends on your delegated SP, accumulated AFIT tokens, rewarded post count and recent rewarded activity. -- Post score: which depends on your activity count, post content, post upvotes, quality comments, moderator review and user rank. - -To improve your user rank, delegate more, pile up more AFIT tokens, and post more. -To improve your post score, get to the max activity count, work on improving your post content, improve your user rank, engage with the community to get more upvotes and quality comments. - -Actifit is now a Steem Witness. If you believe in our project, consider [voting for us](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=1) - -![rulersig2.jpg](https://cdn.steemitimages.com/DQmXrZz658YfMQBXNTA12rmbzqWXASfaGcNSqatJJ2ba7NR/rulersig2.jpg) -Vote for [Actifit as a Witness](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=1) -Chat with us on [discord](https://discord.gg/aHtcA6r) | Visit our [website](https://actifit.io/) -[Download on playstore](https://bit.ly/actifit-app) | [Download on app store](https://bit.ly/actifit-ios) +Congrats on providing **Proof of Activity** via your Actifit report! +{lucky_reward} +You have accordingly been rewarded {token_count} AFIT tokens for your effort in reaching {step_count} activity, as well as your user rank and report quality! +You also received an {weight}% upvote via @actifit account. {exchange_vote} +Actifit rewards and upvotes are based on your: +- User rank: which depends on your delegated SP, accumulated AFIT tokens, rewarded post count and recent rewarded activity. +- Post score: which depends on your activity count, post content, post upvotes, quality comments, moderator review and user rank. + +To improve your user rank, delegate more, pile up more AFIT tokens, and post more. +To improve your post score, get to the max activity count, work on improving your post content, improve your user rank, engage with the community to get more upvotes and quality comments. + +Actifit is now a Steem Witness. If you believe in our project, consider [voting for us](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=1) + +![rulersig2.jpg](https://cdn.steemitimages.com/DQmXrZz658YfMQBXNTA12rmbzqWXASfaGcNSqatJJ2ba7NR/rulersig2.jpg) +Vote for [Actifit as a Witness](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=1) +Chat with us on [discord](https://discord.gg/aHtcA6r) | Visit our [website](https://actifit.io/) +[Download on playstore](https://bit.ly/actifit-app) | [Download on app store](https://bit.ly/actifit-ios) [FAQs](https://steemit.com/actifit/@katerinaramm/actifit-app-or-rewarding-fitness-activity-with-tokens-and-steemit-upvotes-faqs) | [Text Tutorial](https://steemit.com/utopian-io/@katerinaramm/tutorial-for-actifit-app-android) | [Video Tutorial](https://youtu.be/tqkaDoonyvI) \ No newline at end of file diff --git a/curation-bot.js b/curation-bot.js index d54e607..fb68f77 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -8,6 +8,7 @@ const MongoClient = require('mongodb').MongoClient; const cheerio = require('cheerio') const axios = require('axios'); +const request = require("request"); var account = null; var last_trans = 0; @@ -28,6 +29,13 @@ var reward_sys_version = 'v0.2'; var error_sent = false; +var steem_price = 1; // This will get overridden with actual prices if a price_feed_url is specified in settings +var sbd_price = 1; // This will get overridden with actual prices if a price_feed_url is specified in settings + +var STEEMIT_100_PERCENT = 10000; +var STEEMIT_VOTE_REGENERATION_SECONDS = (5 * 60 * 60 * 24); +var HOURS = 60 * 60; + //keep alive var http = require("http"); setInterval(function() { @@ -146,6 +154,13 @@ var moderator_list; var skippable_posts; +var afit_steem_upvote_list; + +var cur_afit_price; + +var helping_accounts_votes = 0; + + // Check if bot state has been saved to disk, in which case load it if (fs.existsSync('state.json')) { var state = JSON.parse(fs.readFileSync("state.json")); @@ -181,6 +196,11 @@ MongoClient.connect(url, function(err, client) { collection = db.collection(collection_name); //only start the process once we connected to the DB startProcess(); + + // Load updated STEEM and SBD prices every 30 minutes + loadPrices(); + setInterval(loadPrices, 30 * 60 * 1000); + //updateUserTokens(); } else { utils.log(err, 'api'); @@ -202,6 +222,59 @@ var lastIterationCount = 0; let queryCount = 0; +let properties, rewardFund, rewardBalance, recentClaims, totalSteem, totalVests, votePowerReserveRate, sbd_print_percentage; + + +//handles grabbing the vote value /STEEM + function getVoteValue(voteWeight, account, currentVotingPower, steem_price) { + if (rewardBalance && recentClaims && steem_price && votePowerReserveRate) { + let voteValue = getVoteRShares(voteWeight, account, currentVotingPower * 100) + * rewardBalance / recentClaims + * steem_price; + + return voteValue; + } + } + //calculate voting value based on rshares contribution + function getVoteRShares (voteWeight, account, power) { + + let effective_vesting_shares = Math.round(getVestingShares(account) * 1000000); + let voting_power = account.voting_power; + let weight = voteWeight * 100; + let last_vote_time = new Date((account.last_vote_time) + 'Z'); + + let elapsed_seconds = (new Date() - last_vote_time) / 1000; + + let regenerated_power = Math.round((STEEMIT_100_PERCENT * elapsed_seconds) / STEEMIT_VOTE_REGENERATION_SECONDS); + + let current_power = power || Math.min(voting_power + regenerated_power, STEEMIT_100_PERCENT); + let max_vote_denom = votePowerReserveRate * STEEMIT_VOTE_REGENERATION_SECONDS / (60 * 60 * 24); + let used_power = Math.round((current_power * weight) / STEEMIT_100_PERCENT); + used_power = Math.round((used_power + max_vote_denom - 1) / max_vote_denom); + + let rshares = Math.round((effective_vesting_shares * used_power) / (STEEMIT_100_PERCENT)) + + return rshares; + } + //grab account vesting shares value + function getVestingShares(account) { + var effective_vesting_shares = parseFloat(account.vesting_shares.replace(" VESTS", "")) + + parseFloat(account.received_vesting_shares.replace(" VESTS", "")) + - parseFloat(account.delegated_vesting_shares.replace(" VESTS", "")); + return effective_vesting_shares; + } + //handles display vote value in USD + function getVoteValueUSD(voteWeight, account, currentVotingPower, sbd_price) { + let vote_value = getVoteValue(voteWeight, account, currentVotingPower, steem_price); + const steempower_value = vote_value * 0.5 + const sbd_print_percentage_half = (0.5 * sbd_print_percentage) + const sbd_value = vote_value * sbd_print_percentage_half + const steem_value = vote_value * (0.5 - sbd_print_percentage_half) + let vote_value_usd = ((sbd_value * sbd_price) + steem_value + steempower_value).toFixed(3); + return vote_value_usd + } + + async function startProcess() { if(!botNames) botNames = await utils.loadBots(); @@ -237,6 +310,10 @@ async function startProcess() { //} } return;*/ + + + + //utils.updateSteemVariables(); if (account && !skip && !is_voting && passedOneDay) { // Load the current voting power of the account @@ -250,11 +327,31 @@ async function startProcess() { // We are at voting power kick start - time to vote! //utils.log(vp >= parseFloat(config.vp_kickstart)/100); if (vp >= parseFloat(config.vp_kickstart)/100 || config.testing) { + // Check if there are any rewards to claim before voting if (!config.testing){ claimRewards(); } - + + + //reset number of helping votes case + helping_accounts_votes = 0; + + + // Load Steem global variables + + properties = await steem.api.getDynamicGlobalPropertiesAsync(); + //grab reward fund data + rewardFund = await steem.api.getRewardFundAsync("post"); + rewardBalance = parseFloat(rewardFund.reward_balance.replace(" STEEM", "")); + recentClaims = rewardFund.recent_claims; + + totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]); + totalVests = Number(properties.total_vesting_shares.split(' ')[0]); + + votePowerReserveRate = properties.vote_power_reserve_rate; + sbd_print_percentage = properties.sbd_print_rate / 10000; + utils.log('lets vote'); skip = true; @@ -273,6 +370,16 @@ async function startProcess() { } utils.log(moderator_list); + //grab list of users exchanging AFIT for STEEM upvotes who were not processed yet by oldest + afit_steem_upvote_list = await db.collection('exchange_afit_steem').find({upvote_processed: {$in: [null, false, 'false']}}).sort({'date': 1}).toArray(); + + console.log('afit_steem_upvote_list'); + console.log(afit_steem_upvote_list); + + //grab AFIT token price + cur_afit_price = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next(); + console.log('curAfitPrice:'+cur_afit_price.unit_price_usd); + //grab list of skippable posts skippable_posts = await db.collection('posts_to_skip').find().toArray(); @@ -280,6 +387,10 @@ async function startProcess() { console.log(skippable_posts); var query = {tag: config.main_tag, limit: 100}; + + if (config.testing){ + query.limit = config.max_query_count; + } votePosts = Array(); processVotes(query, false); }else{ @@ -302,6 +413,68 @@ async function startProcess() { utils.log('Loading account data...'); else utils.log('Voting... or waiting for a day to pass'); } + + +function loadPrices() { + if(config.price_source == 'coinmarketcap') { + // Load the price feed data + request.get('https://api.coinmarketcap.com/v1/ticker/steem/', function (e, r, data) { + try { + steem_price = parseFloat(JSON.parse(data)[0].price_usd); + + utils.log("Loaded STEEM price: " + steem_price); + } catch (err) { + utils.log('Error loading STEEM price: ' + err); + } + }); + + // Load the price feed data + request.get('https://api.coinmarketcap.com/v1/ticker/steem-dollars/', function (e, r, data) { + try { + sbd_price = parseFloat(JSON.parse(data)[0].price_usd); + + utils.log("Loaded SBD price: " + sbd_price); + } catch (err) { + utils.log('Error loading SBD price: ' + err); + } + }); + } else if (config.price_source && config.price_source.startsWith('http')) { + request.get(config.price_source, function (e, r, data) { + try { + sbd_price = parseFloat(JSON.parse(data).sbd_price); + steem_price = parseFloat(JSON.parse(data).steem_price); + + utils.log("Loaded STEEM price: " + steem_price); + utils.log("Loaded SBD price: " + sbd_price); + } catch (err) { + utils.log('Error loading STEEM/SBD prices: ' + err); + } + }); + } else { + // Load STEEM price in BTC from bittrex and convert that to USD using BTC price in coinmarketcap + request.get('https://api.coinmarketcap.com/v1/ticker/bitcoin/', function (e, r, data) { + request.get('https://bittrex.com/api/v1.1/public/getticker?market=BTC-STEEM', function (e, r, btc_data) { + try { + steem_price = parseFloat(JSON.parse(data)[0].price_usd) * parseFloat(JSON.parse(btc_data).result.Last); + utils.log('Loaded STEEM Price from Bittrex: ' + steem_price); + } catch (err) { + utils.log('Error loading STEEM price from Bittrex: ' + err); + } + }); + + request.get('https://bittrex.com/api/v1.1/public/getticker?market=BTC-SBD', function (e, r, btc_data) { + try { + sbd_price = parseFloat(JSON.parse(data)[0].price_usd) * parseFloat(JSON.parse(btc_data).result.Last); + utils.log('Loaded SBD Price from Bittrex: ' + sbd_price); + } catch (err) { + utils.log('Error loading SBD price from Bittrex: ' + err); + } + }); + }); + } +} + + //var post_scores = []; function processVotes(query, subsequent) { @@ -344,10 +517,12 @@ function processVotes(query, subsequent) { query['start_author'] = post.author; } - // Make sure the post is older than config time - if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { - utils.log('This post is too new for a vote: ' + post.url); - continue; + if (!config.testing){ + // Make sure the post is older than config time + if (new Date(post.created) >= new Date(new Date().getTime() - (config.min_hours * 60 * 60 * 1000))) { + utils.log('This post is too new for a vote: ' + post.url); + continue; + } } // Check if the bot already voted on this post @@ -374,10 +549,10 @@ function processVotes(query, subsequent) { } // Check if post category is main tag - if (post.category != config.main_tag) { + /*if (post.category != config.main_tag) { utils.log('Post does not match category tag. ' + post.url); continue; - } + }*/ // Check if this post has been flagged by any flag signal accounts if(config.flag_signal_accounts) { @@ -446,90 +621,92 @@ function processVotes(query, subsequent) { //we need to fetch the proper json_metadata from our own DB to ensure those have not been changed - let ver_url = config.api_url + "fetchVerifiedPost"; - let critical_fields = ['step_count', 'actiCrVal', 'actifitUserID']; + if (!config.testing){ - let incons_detected = false; - let incons_field = ''; - - try{ - var verf_res = await axios.get(ver_url, { - params:{ - author: post.author, - permlink: post.permlink - } - }); + let ver_url = config.api_url + "fetchVerifiedPost"; + let critical_fields = ['step_count', 'actiCrVal', 'actifitUserID']; - //let's compare mission critical data to find if manipulation was done - let auth_meta = verf_res.data.json_metadata; + let incons_detected = false; + let incons_field = ''; - //if either stored or current metadata is non-empty we need to investigate further - if (auth_meta != '' || post.json_metadata != ''){ - //check all critical values - critical_fields.some(function(element) { - //initialize incons field in case we find a match (or lack of) - incons_field = element; - let stored_meta = eval("auth_meta."+element); - let new_meta = eval("post.json."+element); - /*console.log('stored_meta'); - console.log(stored_meta); - console.log('new_meta'); - console.log(new_meta);*/ - //if old data is not empty - if (typeof stored_meta != 'undefined' && stored_meta != ''){ - if (stored_meta instanceof Array ){ - if (new_meta instanceof Array){ - //our arrays are single valued, compare first entry - if (stored_meta[0] != new_meta[0]){ - //different value, manipulation + try{ + var verf_res = await axios.get(ver_url, { + params:{ + author: post.author, + permlink: post.permlink + } + }); + + //let's compare mission critical data to find if manipulation was done + let auth_meta = verf_res.data.json_metadata; + + //if either stored or current metadata is non-empty we need to investigate further + if (auth_meta != '' || post.json_metadata != ''){ + //check all critical values + critical_fields.some(function(element) { + //initialize incons field in case we find a match (or lack of) + incons_field = element; + let stored_meta = eval("auth_meta."+element); + let new_meta = eval("post.json."+element); + /*console.log('stored_meta'); + console.log(stored_meta); + console.log('new_meta'); + console.log(new_meta);*/ + //if old data is not empty + if (typeof stored_meta != 'undefined' && stored_meta != ''){ + if (stored_meta instanceof Array ){ + if (new_meta instanceof Array){ + //our arrays are single valued, compare first entry + if (stored_meta[0] != new_meta[0]){ + //different value, manipulation + incons_detected = true; + return true; + } + }else{ + //different object types, manipulation + incons_detected = true; + return true; + } + }else{ + if (stored_meta != new_meta){ + //different value, manipulation + incons_detected = true; + return true; + } + } + }else{ + //original data is empty, need to check if new data is not + if (typeof new_meta != 'undefined' && new_meta != ''){ incons_detected = true; return true; } - }else{ - //different object types, manipulation - incons_detected = true; - return true; } - }else{ - if (stored_meta != new_meta){ - //different value, manipulation - incons_detected = true; - return true; - } - } - }else{ - //original data is empty, need to check if new data is not - if (typeof new_meta != 'undefined' && new_meta != ''){ - incons_detected = true; - return true; - } - } - }); + }); + } + }catch(verf_err){ + console.log('error finding matching post on DB'); + console.dir(verf_err); + } + //console.log('data inconsistency: ' + incons_detected); + //check if we found metadata issue + if (incons_detected){ + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('problematic field:' + incons_field); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + console.log('***********************'); + //we've got a problem, skip this post/guy. We might want to report too. + continue; } - }catch(verf_err){ - console.log('error finding matching post on DB'); - console.dir(verf_err); - } - //console.log('data inconsistency: ' + incons_detected); - //check if we found metadata issue - if (incons_detected){ - console.log('***********************'); - console.log('***********************'); - console.log('***********************'); - console.log('***********************'); - console.log('***********************'); - console.log('problematic field:' + incons_field); - console.log('***********************'); - console.log('***********************'); - console.log('***********************'); - console.log('***********************'); - console.log('***********************'); - //we've got a problem, skip this post/guy. We might want to report too. - continue; } - //check if the post has an encryption key val, and ensure it is the proper one if (post.json.actiCrVal){ var txt_to_encr = post.author + post.permlink + post.json.step_count ; @@ -912,7 +1089,7 @@ function processVotes(query, subsequent) { //if this is the first try, or the new count of posts is bigger than the one before, let's try adding again - if (!subsequent || votePosts.length > lastIterationCount || queryCount < config.max_query_count){ + if (!config.testing && (!subsequent || votePosts.length > lastIterationCount || queryCount < config.max_query_count)){ //update last count lastIterationCount = votePosts.length; @@ -926,10 +1103,14 @@ function processVotes(query, subsequent) { if (votePosts.length > 0) { utils.log(votePosts.length + ' posts to vote...'); var vote_data = utils.calculateVotes(votePosts, config.vote_weight); - votePosts.sort(function(post1, post2) { - //Sort posts by reverse score, so as when popping them we get sorted by highest - return post1.post_score - post2.post_score; - }); + + + //if (!config.testing){ + votePosts.sort(function(post1, post2) { + //Sort posts by reverse score, so as when popping them we get sorted by highest + return post1.post_score - post2.post_score; + }); + //} utils.log(vote_data.power_per_vote + ' power per full vote.'); @@ -995,20 +1176,85 @@ function processVotes(query, subsequent) { utils.log('after'); utils.log(votePosts[lucky_winner_id].post_score); - /********************* proceed with STEEM upvotes ************************/ - + /***************** Check for AFIT/STEEM upvote exchange ******************/ + try{ - var tot_weight = 0; - for (var xx=0;xx user_post.author === cur_upvote_entry.user); + console.log('matching post'); + console.log(result); + if (result != null){ + matched_exchanges += 1; + //found a match, need to increase rewards according to AFIT pay + //calculate total paid AFIT in USD (which should be equal to a 65% reward, since Actifit removes 10% benefic, and author reward removes 75% + let usd_val_no_benef = parseFloat(cur_upvote_entry.paid_afit) * parseFloat(cur_afit_price.unit_price_usd); + + //expand the USD val to take into consideration 75% curation reward + let usd_val_no_curation = usd_val_no_benef * 0.75 / 0.65 + + //final upvote value after avoiding deductions + let usd_val = usd_val_no_benef / 0.65 + + //emulate proper voting power to give user matching rewards + let user_added_vote_weight = usd_val * 100 / full_vote_value; + + let entry_index = votePosts.findIndex( user_post => user_post.author === cur_upvote_entry.user); + + //decrease by 1% since assisting accounts will vote too (pay & funds) only if we still have room to use them + if (helping_accounts_votes < config.max_helping_votes){ + user_added_vote_weight -= 1; + + helping_accounts_votes += 1; + + votePosts[entry_index].helperVotes = true; + } + + user_added_vote_weight = user_added_vote_weight.toFixed(2); + + votePosts[entry_index].additional_vote_weight = user_added_vote_weight * 100; + votePosts[entry_index].afit_swapped = cur_upvote_entry.paid_afit; + console.log('Additional Vote Weight for AFIT/STEEM Upvote Exchange: '+votePosts[entry_index].author + ' ' + votePosts[entry_index].url); + console.log(votePosts[entry_index].additional_vote_weight); + + //we need to set this transaction as processed via upvote + cur_upvote_entry.additional_vote_weight = votePosts[entry_index].additional_vote_weight / 100; + cur_upvote_entry.usd_val_no_benef = +usd_val_no_benef; + cur_upvote_entry.usd_val_no_curation = +usd_val_no_curation.toFixed(2); + cur_upvote_entry.usd_val = +usd_val.toFixed(2); + cur_upvote_entry.upvote_processed = true; + cur_upvote_entry.reward_cycle_ID = reward_cycle_ID; + db.collection('exchange_afit_steem').save(cur_upvote_entry); + + } + } + console.log('done going through pending AFIT to STEEM upvote exchange'); + + }catch(err){ + utils.log(err); } - utils.log('total weight consumed'+tot_weight); - //if (!config.testing){ - votingProcess(votePosts, vote_data.power_per_vote); - //} + /********************* proceed with STEEM upvotes ************************/ + + votingProcess(votePosts, vote_data.power_per_vote); + } else { utils.log('No posts to vote...'); if(!error_sent) { @@ -1060,6 +1306,13 @@ function sendVote(post, retries, power_per_vote) { var token_count = post.post_score;//parseFloat(post.rate_multiplier)*100; var vote_weight = Math.floor(post.rate_multiplier * power_per_vote); + console.log('vote weight:'+vote_weight); + + //if user had paid AFIT for extra STEEM upvotes, add this to their upvote value + if (post.additional_vote_weight){ + vote_weight += post.additional_vote_weight + console.log('new vote weight:'+vote_weight); + } post_rank += 1; utils.log('|#'+post_rank+'|@'+post.author+'|'+ post.json.step_count +'|'+token_count+' Tokens|'+utils.format(vote_weight / 100)+'%|[post](https://www.steemit.com'+post.url+')'); @@ -1086,6 +1339,30 @@ function sendVote(post, retries, power_per_vote) { resolve(''); } }else{ + //vote first using pay and funds accounts only if we have an AFIT/STEEM exchange operation and we have room to upvote using helping accounts + if (post.additional_vote_weight && post.helperVotes){ + let vote_percent_add_accounts = 5000;//at 50%: 5000 + try{ + steem.broadcast.vote(config.full_pay_posting_key, config.full_pay_benef_account, post.author, post.permlink, vote_percent_add_accounts, function (err, result) { + utils.log('voting with '+config.full_pay_benef_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); + if (!err && result) { + utils.log(err, result); + } + }); + }catch(err){ + utils.log(err); + } + try{ + steem.broadcast.vote(config.pay_account_post_key, config.pay_account, post.author, post.permlink, vote_percent_add_accounts, function (err, result) { + utils.log('voting with '+config.pay_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); + if (!err && result) { + utils.log(err, result); + } + }); + }catch(err){ + utils.log(err); + } + } steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { if (!err && result) { utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); @@ -1193,6 +1470,16 @@ function sendComment(post, retries, vote_weight) { //adding proper meta content for later relevant reward via afit_tokens data var jsonMetadata = { tags: ['actifit'], app: 'actifit/v'+version, activity_count: post_step_count, user_rank: post.user_rank, content_score: post.content_score, media_score: post.media_score, upvote_score: post.upvote_score, comment_score: post.comment_score, user_rank_score: post.user_rank_score, moderator_score: post.moderator_score, post_activity_score: post.activity_score, afit_tokens: token_count, post_upvote: vote_weight }; + //if this reward contains an exchange amount, list it here + if (post.additional_vote_weight != null){ + jsonMetadata.promoted_post = true; + jsonMetadata.additional_vote_weight = post.additional_vote_weight; + console.log('new vote weight:'+vote_weight); + content = content.replace(/\{exchange_vote}/g,'**'+utils.format(post.additional_vote_weight/100)+'% of this upvote value is a result of an exchange transaction you performed for '+post.afit_swapped+' AFIT tokens !**'); + }else{ + content = content.replace(/\{exchange_vote}/g,'') + } + //if user is lucky winner, add a relevant message if (typeof post.lucky_winner != 'undefined' && post.lucky_winner != '' && post.lucky_winner != 'undefined'){ @@ -1202,7 +1489,6 @@ function sendComment(post, retries, vote_weight) { content = content.replace(/\{lucky_reward}/g,'') } - if (!config.testing){ // Broadcast the comment steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { @@ -1228,8 +1514,8 @@ function sendComment(post, retries, vote_weight) { }); }else{ utils.log('comment'); - utils.log(content); - utils.log(jsonMetadata); + console.log(content); + console.log(jsonMetadata); resolve(''); } }else{ From 69613206edee2ddbc3476702a22f564b6c0afd38 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 18 Mar 2019 12:48:45 +0200 Subject: [PATCH 077/193] Add endpoint for user trx history Add endpoint to fetch a user's transaction history --- app.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app.js b/app.js index 9714891..1ccff89 100644 --- a/app.js +++ b/app.js @@ -1160,6 +1160,12 @@ app.get('/userHasPendingTokenSwap/:user', async function(req, res){ res.send({user_pending_swap: user_pending_swap}); }); +/* end point finding user's historical AFIT token swap */ +app.get('/getUserTokenSwapHistory/:user', async function(req, res){ + let user_token_swap_hist = await db.collection('exchange_afit_steem').find({user: req.params.user},{fields : { _id:0} }).sort({'date': -1}).toArray(); + res.send({userTokenSwapHist: user_token_swap_hist}); +}); + /* end point for getting number of AFIT -> STEEM upvotes pending exchanges */ app.get('/getPendingTokenSwapTransCount/', async function(req, res){ let tokenSwapTrans = await db.collection('exchange_afit_steem').find({upvote_processed: {$in: [null, false, 'false']}}).sort({'date': 1}).toArray(); From 81cfce5befbcc7c036b224b77e3a50a75cfc3e25 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 18 Mar 2019 12:50:34 +0200 Subject: [PATCH 078/193] Parametrize Steem Node change approach of Steem nodes to be a const across the utils file --- utils.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/utils.js b/utils.js index 5289576..4e6906b 100644 --- a/utils.js +++ b/utils.js @@ -4,13 +4,14 @@ var _ = require('lodash'); const axios = require('axios'); const dsteem = require('dsteem'); const moment = require('moment') -const client = new dsteem.Client('https://api.steemit.com'); +const steem_node = 'https://api.steemit.com';//'https://api.steem.house'; +const client = new dsteem.Client(steem_node); var config; let th_id = -1; -steem.api.setOptions({ url: 'https://api.steemit.com' }); +steem.api.setOptions({ url: steem_node }); var STEEMIT_100_PERCENT = 10000; var STEEMIT_VOTE_REGENERATION_SECONDS = (5 * 60 * 60 * 24); @@ -86,7 +87,7 @@ var HOURS = 60 * 60; var data={"jsonrpc":"2.0","id":1,"method":"condenser_api.get_account_count","params":{}}; //return new Promise(function(fulfill,reject){ //var request = require("request"); - let location = "https://api.steemit.com"; + let location = steem_node; var response = await axios.post(location, {"jsonrpc":"2.0","id":1,"method":"rc_api.find_rc_accounts","params":{"accounts":[account_name]}}); console.log(response.data.result.rc_accounts); @@ -692,8 +693,8 @@ async function lookupAccountPay (){ const ONE_MONTH = 30; const ONE_YEAR = 365; - let start_days = 1; - let lookup_days = ONE_YEAR; + let start_days = 2; + let lookup_days = ONE_MONTH; let today = moment().utc().startOf('date').toDate() let start = moment(today).subtract(start_days, 'days').toDate() @@ -805,8 +806,6 @@ async function getAccountPayTransactions (account, start, end, period) { console.log(op); }else if (op[0] === 'transfer' && op[1].from === account && (op[1].to !== 'bittrex' && !op[1].to.includes('actifit'))){//skip bittrex and actifit account transfers - console.log('>>>>>><<<<<<<'); - console.log(op[1]); let amountWithCur = op[1].amount; let amount = amountWithCur.split(' ')[0] let cur = amountWithCur.split(' ')[1] From 1f62324b0e1a726dbeb9175ced89f2724b0965f2 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 18 Mar 2019 13:02:23 +0200 Subject: [PATCH 079/193] Prevent vote rewards for banned users Implement a change to check if voter is a banned user, and prevent potential AFIT rewards. --- curation-bot.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/curation-bot.js b/curation-bot.js index fb68f77..ccb86af 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1030,8 +1030,18 @@ function processVotes(query, subsequent) { //grab user's contribution to the upvote pool var upv_tokens = parseInt(vote.rshares); + //skip votes of banned users + var user_banned = false; + for (var n = 0; n < banned_users.length; n++) { + if (vote.voter == banned_users[n].user){ + utils.log('User '+vote.voter+' is banned, skipping his vote on post:' + post.url); + user_banned = true; + break; + } + } + //skip self vote from rewards and make sure this is a positive upvote - if (post.author != vote.voter && upv_tokens>0){ + if (post.author != vote.voter && upv_tokens>0 && !user_banned){ //calculate the percentage of the user's contribution, and allocate him his AFIT tokens share var voter_tokens = upv_tokens / total_post_upv_shares * max_afits; //console.log(voter_tokens); From e0f7f70770dc42717daf28204c9586f3bcd093cf Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 18 Mar 2019 13:05:23 +0200 Subject: [PATCH 080/193] Delay exchange complete status till STEEM upvote - Implement a change so as to only set exchange as complete status when actual STEEM upvote takes place, to prevent cases with premature voting round end - Implement a fix for rounding issue causing decimal points in STEEM upvote value --- curation-bot.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index ccb86af..054c02d 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1239,18 +1239,22 @@ function processVotes(query, subsequent) { user_added_vote_weight = user_added_vote_weight.toFixed(2); - votePosts[entry_index].additional_vote_weight = user_added_vote_weight * 100; + votePosts[entry_index].additional_vote_weight = Math.floor(user_added_vote_weight * 100); votePosts[entry_index].afit_swapped = cur_upvote_entry.paid_afit; console.log('Additional Vote Weight for AFIT/STEEM Upvote Exchange: '+votePosts[entry_index].author + ' ' + votePosts[entry_index].url); console.log(votePosts[entry_index].additional_vote_weight); - //we need to set this transaction as processed via upvote + //we need to set params of this transaction cur_upvote_entry.additional_vote_weight = votePosts[entry_index].additional_vote_weight / 100; cur_upvote_entry.usd_val_no_benef = +usd_val_no_benef; cur_upvote_entry.usd_val_no_curation = +usd_val_no_curation.toFixed(2); cur_upvote_entry.usd_val = +usd_val.toFixed(2); - cur_upvote_entry.upvote_processed = true; + cur_upvote_entry.reward_cycle_ID = reward_cycle_ID; + + cur_upvote_entry.post_author = votePosts[entry_index].author; + cur_upvote_entry.post_permlink = votePosts[entry_index].permlink; + db.collection('exchange_afit_steem').save(cur_upvote_entry); } @@ -1335,6 +1339,19 @@ function sendVote(post, retries, power_per_vote) { return new Promise((resolve, reject) => { if(config.testing){ //resolve(''); + + //console.log(afit_steem_upvote_list); + if (post.additional_vote_weight){ + console.log('Exchange vote'); + //store exchange transaction as complete + let cur_upvote_entry = afit_steem_upvote_list.find( entry => entry.post_author === post.author && + entry.post_permlink === post.permlink && + entry.user === post.author); + console.log(cur_upvote_entry); + cur_upvote_entry.upvote_processed = true; + db.collection('exchange_afit_steem').save(cur_upvote_entry); + } + if(config.comment_location && config.comment){ setTimeout(function () { sendComment(post, 0, vote_weight) @@ -1377,6 +1394,17 @@ function sendVote(post, retries, power_per_vote) { if (!err && result) { utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); + //store exchange transaction as complete + if (post.additional_vote_weight){ + console.log('Exchange vote'); + let cur_upvote_entry = afit_steem_upvote_list.find( entry => entry.post_author === post.author && + entry.post_permlink === post.permlink && + entry.user === post.author); + console.log(cur_upvote_entry); + cur_upvote_entry.upvote_processed = true; + db.collection('exchange_afit_steem').save(cur_upvote_entry); + } + if(config.comment_location && config.comment){ setTimeout(function () { sendComment(post, 0, vote_weight) From d9896fb90eb6dd5f5534fd20ea5fb8098195e150 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 18 Mar 2019 13:11:06 +0200 Subject: [PATCH 081/193] Automate sponsored athlete reward Implement a change to automated daily sponsored athlete rewards for both AFIT and STEEM upvotes --- curation-bot.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 054c02d..43a810e 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -595,7 +595,7 @@ function processVotes(query, subsequent) { user_banned = true; break; } - } + } if (user_banned) continue; //skip any posts that are more than max days old @@ -967,11 +967,16 @@ function processVotes(query, subsequent) { reward_user = post.json.charity[0]; activity_type = 'Charity Post'; note = 'Charity donation via activity by user '+post.author; - } + } + let activity_afit_reward = post.post_score; + //if this is a sponsored athlete, give them special reward + if (config.sponsored_athletes.includes(reward_user)){ + activity_afit_reward = config.sponsored_athlete_afit_reward; + } let post_transaction = { user: reward_user, reward_activity: activity_type, - token_count: post.post_score, + token_count: activity_afit_reward, url: post.url, date: new Date(post.created), note: note, @@ -1029,7 +1034,7 @@ function processVotes(query, subsequent) { //grab user's contribution to the upvote pool var upv_tokens = parseInt(vote.rshares); - + //skip votes of banned users var user_banned = false; for (var n = 0; n < banned_users.length; n++) { @@ -1330,6 +1335,12 @@ function sendVote(post, retries, power_per_vote) { post_rank += 1; utils.log('|#'+post_rank+'|@'+post.author+'|'+ post.json.step_count +'|'+token_count+' Tokens|'+utils.format(vote_weight / 100)+'%|[post](https://www.steemit.com'+post.url+')'); + //if this is a sponsored athlete, give them special reward + if (config.sponsored_athletes.includes(post.author)){ + vote_weight = config.sponsored_athlete_upvote_reward; + } + + if (vote_weight > config.max_vote_per_post){ vote_weight = config.max_vote_per_post; } @@ -1393,7 +1404,7 @@ function sendVote(post, retries, power_per_vote) { steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { if (!err && result) { utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); - + //store exchange transaction as complete if (post.additional_vote_weight){ console.log('Exchange vote'); @@ -1500,6 +1511,11 @@ function sendComment(post, retries, vote_weight) { var token_count = post.post_score;//parseFloat(rate_multiplier)*100; + //if this is a sponsored athlete, give them special reward + if (config.sponsored_athletes.includes(parentAuthor)){ + token_count = config.sponsored_athlete_afit_reward; + } + // Replace variables in the promotion content content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); From 39530a522c2ea37efca0d2ad8a293a669fabab07 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Apr 2019 19:16:09 +0300 Subject: [PATCH 082/193] Implement Exchange Refund Implement feature to enable cancellation of AFIT to STEEM Upvote exchange following 14 day waiting period --- app.js | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/app.js b/app.js index 1ccff89..1ba5c7e 100644 --- a/app.js +++ b/app.js @@ -1304,6 +1304,80 @@ app.get('/performAfitSteemExchange', async function(req, res){ } }) +/* end point handling cancelling outdated exchange transactions for AFIT/STEEM upvote exchange */ +app.get('/cancelOutdatedAfitSteemExchange', async function(req, res){ + //grab list of pending & outdated exchange requests + let startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + let endDate = moment(moment(startDate).utc().subtract(config.exchange_refund_max_days, 'days').toDate()).format('YYYY-MM-DD'); + let transQuery = { + upvote_processed: {$in: [null, false, 'false']}, + date: { + $lte: new Date(endDate) + } + } + let outdatedTokenSwapTrans = await db.collection('exchange_afit_steem').find(transQuery).toArray(); + console.log(outdatedTokenSwapTrans); + + let refundedCount = 0; + + //go through each transaction and cancel it + for(let i = 0, transLen = outdatedTokenSwapTrans.length; i < transLen; i++) { + try{ + //set as processed, and flag as refunded + outdatedTokenSwapTrans[i].refunded = true; + outdatedTokenSwapTrans[i].refund_reason = 'overdue for '+config.exchange_refund_max_days + ' days'; + outdatedTokenSwapTrans[i].upvote_processed = true; + await db.collection('exchange_afit_steem').save(outdatedTokenSwapTrans[i]); + console.log('exchange transaction cancelled'); + }catch(err){ + console.log('unable to cancel exchange transaction'); + res.send({'error': 'Unable to cancel exchange transaction'}); + return; + } + + //send out refunded AFIT tokens + //decrease count + let tokenExchangeTrans = { + user: outdatedTokenSwapTrans[i].user, + reward_activity: 'Refund Exchange AFIT To STEEM Upvote', + token_count: outdatedTokenSwapTrans[i].paid_afit, + note: 'Refund Exchange AFIT To STEEM Upvote due to overdue pending '+config.exchange_refund_max_days + ' days without Actifit report card', + date: new Date(), + } + try{ + console.log(tokenExchangeTrans); + let transaction = await db.collection('token_transactions').insert(tokenExchangeTrans); + console.log('tokens refunded for user'); + }catch(err){ + console.log(err); + res.send({'error': 'Error converting AFIT to STEEM upvotes'}); + return; + } + + let user_info = await grabUserTokensFunc (outdatedTokenSwapTrans[i].user); + console.log(user_info); + let cur_user_token_count = 0; + if (user_info){ + cur_user_token_count = parseFloat(user_info.tokens); + //update current user's token balance & store to db + let new_token_count = cur_user_token_count + parseFloat(outdatedTokenSwapTrans[i].paid_afit); + user_info.tokens = new_token_count; + console.log('new_token_count:'+new_token_count); + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success updating user token count'); + }catch(err){ + console.log(err); + return; + } + } + + refundedCount += 1; + } + //we got here, we're good + res.send({'status': 'Success', 'trans_refunded_count': refundedCount}); +}); + /* end point handling the display of categorized token holders */ app.get('/fetchTokenHoldersByCategory', async function(req, res){ //connect to DB, and identify token holders by category From 699a048b8f88f1952536e16dbd4869f56840c433 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Apr 2019 19:19:35 +0300 Subject: [PATCH 083/193] Switch Helper Vote Percent to Config Switch Helper Voting Accounts Percentage to a config controlled value --- curation-bot.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/curation-bot.js b/curation-bot.js index 43a810e..f2dbc6a 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1379,7 +1379,7 @@ function sendVote(post, retries, power_per_vote) { }else{ //vote first using pay and funds accounts only if we have an AFIT/STEEM exchange operation and we have room to upvote using helping accounts if (post.additional_vote_weight && post.helperVotes){ - let vote_percent_add_accounts = 5000;//at 50%: 5000 + let vote_percent_add_accounts = config.helping_account_percent;//at 50%: 5000 try{ steem.broadcast.vote(config.full_pay_posting_key, config.full_pay_benef_account, post.author, post.permlink, vote_percent_add_accounts, function (err, result) { utils.log('voting with '+config.full_pay_benef_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); From 8241fc3a09621c0283d69f51cf9d6d343d012682 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Apr 2019 19:21:12 +0300 Subject: [PATCH 084/193] Skip Moderator Self Reward Implement a change to prevent moderators from receiving additional rewards for self-vote --- curation-bot.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index f2dbc6a..2f5002f 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -869,9 +869,10 @@ function processVotes(query, subsequent) { /***************** moderator upvote factor ******************/ //check if a moderator upvoted the post to give it better reward + //we need to skip self-votes reward to avoid extra rewarding mods for self-votes post.moderator_score = 0; post.active_votes.some(function(vote){ - if (moderator_list.includes(vote.voter)){ + if (moderator_list.includes(vote.voter) && vote.voter != post.author){ post.moderator_score = parseInt(config.moderator_upvote_factor); //utils.log('found moderator upvote'+vote.voter); return true; @@ -895,7 +896,8 @@ function processVotes(query, subsequent) { } //check if the comment is made by a moderator, if it is we need to reward the moderator - if (moderator_list.includes(comments[cmt_it].author)){ + //we need to skip own comment reward to avoid extra rewarding mods + if (moderator_list.includes(comments[cmt_it].author) && comments[cmt_it].author != post.author){ let comment_transaction = { user: comments[cmt_it].author, reward_activity: 'Moderator Comment', From beb993ad7f9a278dffe80b3fa41ed189dcc4186e Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 7 Apr 2019 19:22:36 +0300 Subject: [PATCH 085/193] Implement Recurring Token Exchange Cleanup Implement recurring calls for the token exchange cleanup to ensure no exchanges remain in queue for more than 2 weeks --- curation-bot.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/curation-bot.js b/curation-bot.js index 2f5002f..9764ea8 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -39,7 +39,18 @@ var HOURS = 60 * 60; //keep alive var http = require("http"); setInterval(function() { + try{ http.get("http://actifitvoter.herokuapp.com"); + //let's also run our token exchange cleanup process + console.log('running cleanup'); + request('https://actifitbot.herokuapp.com/cancelOutdatedAfitSteemExchange', function (error, response, body) { + console.log('cleanup result'); + console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received + console.log('error: '+ error) + }); + }catch(err){ + console.log('error:'+err); + } }, 600000); // every 10 minutes (600000) From 6b3dd9aff05a0f99bc17f0d5a9697e222083c6df Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 10 May 2019 02:10:49 +0300 Subject: [PATCH 086/193] Enable multi-account benefit Enable multi-account benefit from own & alt-account delegation --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 1ba5c7e..be169e0 100644 --- a/app.js +++ b/app.js @@ -533,7 +533,7 @@ app.get('/getRank/:user', async function (req, res) { //get original user delegation amount userDelegations = await activeDelegationFunc(delegator_info.delegator); if (userDelegations != null){ - delegSP = userDelegations.steem_power; + delegSP += userDelegations.steem_power; } } } From 7b989c67d17f7ee6ac0fefa28b08093ad4fb99a7 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 10 May 2019 02:12:42 +0300 Subject: [PATCH 087/193] Move AFIT S-E to AFIT Actifit wallet Implement new functionality to move AFIT S-E to AFIT Actifit wallet --- app.js | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/app.js b/app.js index be169e0..2833817 100644 --- a/app.js +++ b/app.js @@ -931,6 +931,88 @@ storeReferralReward = async function (req){ return refRewarded; }; + +//function handles the process of confirming AFIT S-E receipt into proper account, and increases AFIT amount held in power mode +app.get('/confirmAFITSEReceipt', async function(req,res){ + if (!req.query.user){ + res.send('{}'); + }else{ + //keeping request alive to avoid timeouts + let intID = setInterval(function(){ + res.write(' '); + }, 6000); + let afit_amount = 0; + try{ + //attempt to find matching transaction + let targetUser = req.query.user; + let match_trx = await utils.confirmSEAFITReceived(targetUser); + console.log(match_trx); + //we found a match + if (match_trx){ + //query to see if entry already stored + let tokenExchangeTransQuery = { + user: targetUser, + se_trx_ref: match_trx.txid + } + //store the transaction to the user's profile + let tokenExchangeTrans = { + user: targetUser, + reward_activity: 'Move AFIT SE to Actifit Wallet', + token_count: parseFloat(match_trx.quantity), + se_trx_ref: match_trx.txid, + date: new Date(), + } + try{ + console.log(tokenExchangeTrans); + //insert the query ensuring we do not write it twice + let transaction = await db.collection('token_transactions').update(tokenExchangeTransQuery, tokenExchangeTrans, { upsert: true }); + let trans_res = transaction.result; + console.log(trans_res); + /*console.log('nMatched:'+trans_res.nMatched); + console.log('nUpserted:'+trans_res.upserted); + console.log('nModified:'+trans_res.nModified);*/ + if (trans_res.upserted){ + //we have a new entry, increase user token count + + let user_info = await grabUserTokensFunc (targetUser); + + let cur_user_token_count = 0; + if (user_info){ + cur_user_token_count = parseFloat(user_info.tokens); + //update current user's token balance & store to db + afit_amount = parseFloat(match_trx.quantity); + let new_token_count = cur_user_token_count + parseFloat(afit_amount); + user_info.tokens = new_token_count; + console.log('new_token_count:'+new_token_count); + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success adding AFIT tokens to user balance'); + }catch(err){ + console.log(err); + return; + } + } + }else{ + //do nothing + } + }catch(err){ + console.log(err); + res.write(JSON.stringify({'error': 'Error adding AFIT tokens to user balance'})); + res.end(); + return; + } + } + }catch(err){ + console.log(err); + } + //we're done, let's clear our running interval + clearInterval(intID); + //send response with confirming AFIT power up + res.write(JSON.stringify({'afit_se_power': 'success', 'afit_amount': afit_amount})); + res.end(); + } +}); + //function handles the process of confirming payment receipt, and then proceeds with account creation, reward and delegation app.get('/confirmPayment', async function(req,res){ if (req.query.confirm_payment_token != config.confirmPaymentToken){ From 5d7e51a171e4064185eff363bdee05cce7ba71d0 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 10 May 2019 02:16:01 +0300 Subject: [PATCH 088/193] Move AFIT S-E to AFIT Actifit Implement new functionality to move AFIT S-E to AFIT Actifit wallet --- utils.js | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/utils.js b/utils.js index 4e6906b..5ad8140 100644 --- a/utils.js +++ b/utils.js @@ -109,6 +109,42 @@ var HOURS = 60 * 60; //}); } + + //function handles confirming if AFIT from SE were received + async function confirmSEAFITReceived (targetUser) { + getConfig(); + return new Promise((resolve, reject) => { + th_id = setInterval(async function(){ + console.log('Check AFIT Power Up'); + //let's call the service by S-E + let url = new URL(config.steem_engine_trans_acct_his); + console.log(config.steem_engine_trans_acct_his); + //connect with our service to confirm AFIT received to proper wallet + try{ + let se_connector = await fetch(url); + let trx_entries = await se_connector.json(); + + let match_trx; + + //check if we have a proper entry matching user transfer + if (match_trx = trx_entries.find(trx => trx.from == targetUser)) { + //found match, let's make sure transaction is recent enough + console.log('found match'); + paymentFound = true; + if (paymentFound){ + //need to look again + console.log('found'); + clearInterval(th_id); + resolve(match_trx); + } + } + }catch(err){ + console.log(err); + } + }, 5000); + }); + } + //function handles confirming if payment was received async function confirmPaymentReceived (req) { @@ -930,5 +966,6 @@ async function steemPowerToVests (steemPower) { createAccount: createAccount, delegateToAccount: delegateToAccount, confirmPaymentReceived: confirmPaymentReceived, - confirmPaymentReceivedPassword: confirmPaymentReceivedPassword + confirmPaymentReceivedPassword: confirmPaymentReceivedPassword, + confirmSEAFITReceived: confirmSEAFITReceived, } From e271e6ef6ae5865b7ffbad649914bb0f1e478408 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 10 May 2019 02:17:42 +0300 Subject: [PATCH 089/193] Bulk update AFIT SE to Actifit Wallet Adding script to bulk update AFIT SE to Actifit wallet for any missed entries --- app.js | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/app.js b/app.js index 2833817..b81b789 100644 --- a/app.js +++ b/app.js @@ -932,6 +932,88 @@ storeReferralReward = async function (req){ }; +//function handles the process of confirming AFIT S-E receipt into proper account, and increases AFIT amount held in power mode +app.get('/confirmAFITSEReceipt', async function(req,res){ + if (!req.query.user){ + res.send('{}'); + }else{ + //keeping request alive to avoid timeouts + let intID = setInterval(function(){ + res.write(' '); + }, 6000); + let afit_amount = 0; + try{ + //attempt to find matching transaction + let targetUser = req.query.user; + let match_trx = await utils.confirmSEAFITReceived(targetUser); + console.log(match_trx); + //we found a match + if (match_trx){ + //query to see if entry already stored + let tokenExchangeTransQuery = { + user: targetUser, + se_trx_ref: match_trx.txid + } + //store the transaction to the user's profile + let tokenExchangeTrans = { + user: targetUser, + reward_activity: 'Move AFIT SE to Actifit Wallet', + token_count: parseFloat(match_trx.quantity), + se_trx_ref: match_trx.txid, + date: new Date(), + } + try{ + console.log(tokenExchangeTrans); + //insert the query ensuring we do not write it twice + let transaction = await db.collection('token_transactions').update(tokenExchangeTransQuery, tokenExchangeTrans, { upsert: true }); + let trans_res = transaction.result; + console.log(trans_res); + /*console.log('nMatched:'+trans_res.nMatched); + console.log('nUpserted:'+trans_res.upserted); + console.log('nModified:'+trans_res.nModified);*/ + if (trans_res.upserted){ + //we have a new entry, increase user token count + + let user_info = await grabUserTokensFunc (targetUser); + + let cur_user_token_count = 0; + if (user_info){ + cur_user_token_count = parseFloat(user_info.tokens); + //update current user's token balance & store to db + afit_amount = parseFloat(match_trx.quantity); + let new_token_count = cur_user_token_count + parseFloat(afit_amount); + user_info.tokens = new_token_count; + console.log('new_token_count:'+new_token_count); + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success adding AFIT tokens to user balance'); + }catch(err){ + console.log(err); + return; + } + } + }else{ + //do nothing + } + }catch(err){ + console.log(err); + res.write(JSON.stringify({'error': 'Error adding AFIT tokens to user balance'})); + res.end(); + return; + } + } + }catch(err){ + console.log(err); + } + //we're done, let's clear our running interval + clearInterval(intID); + //send response with confirming AFIT power up + res.write(JSON.stringify({'afit_se_power': 'success', 'afit_amount': afit_amount})); + res.end(); + } +}); + + //function handles the process of confirming AFIT S-E receipt into proper account, and increases AFIT amount held in power mode app.get('/confirmAFITSEReceipt', async function(req,res){ if (!req.query.user){ From 1785f00fe14e77f7914e26e20b00f092dd5cded4 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 10 May 2019 02:19:13 +0300 Subject: [PATCH 090/193] Append additional transfer data calculation Append additional transfer data calculation for account pay transactions --- utils.js | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/utils.js b/utils.js index 5ad8140..cf29d6b 100644 --- a/utils.js +++ b/utils.js @@ -109,7 +109,8 @@ var HOURS = 60 * 60; //}); } - + + //function handles confirming if AFIT from SE were received async function confirmSEAFITReceived (targetUser) { getConfig(); @@ -145,7 +146,6 @@ var HOURS = 60 * 60; }); } - //function handles confirming if payment was received async function confirmPaymentReceived (req) { getConfig(); @@ -697,6 +697,9 @@ let authorRewardedSTEEM = 0 let accountSTEEMTransfer = 0; let accountSBDTransfer = 0; +let accountSTEEMTransferIn = 0 +let accountSBDTransferIn = 0 + let limit = 5000; let txStart = -1; let opsArr = []; @@ -718,6 +721,9 @@ function resetVals(){ accountSTEEMTransfer = 0 accountSBDTransfer = 0 + + accountSBDTransferIn = 0 + accountSTEEMTransferIn = 0 } //lookupAccountPay(); @@ -729,7 +735,7 @@ async function lookupAccountPay (){ const ONE_MONTH = 30; const ONE_YEAR = 365; - let start_days = 2; + let start_days = 0; let lookup_days = ONE_MONTH; let today = moment().utc().startOf('date').toDate() @@ -750,6 +756,14 @@ async function lookupAccountPay (){ txStart = -1; console.log('***********append actifit.pay rewards**********') await getAccountPayTransactions('actifit.pay', start, to, lookup_days); + + txStart = -1; + console.log('***********append actifit.exchange rewards**********') + await getAccountPayTransactions('actifit.exchange', start, to, lookup_days); + + txStart = -1; + console.log('***********append actifit.signup rewards**********') + await getAccountPayTransactions('actifit.signup', start, to, lookup_days); } async function getAccountPayTransactions (account, start, end, period) { @@ -850,6 +864,16 @@ async function getAccountPayTransactions (account, start, end, period) { }else{ accountSTEEMTransfer += parseFloat(amount) } + }else if (op[0] === 'transfer' && op[1].to === account && + (!op[1].from.includes('actifit'))){//skip intra account transfer + let amountWithCur = op[1].amount; + let amount = amountWithCur.split(' ')[0] + let cur = amountWithCur.split(' ')[1] + if (cur == 'SBD'){ + accountSBDTransferIn += parseFloat(amount) + }else{ + accountSTEEMTransferIn += parseFloat(amount) + } } } else if (date < end){ break @@ -902,13 +926,13 @@ async function getAccountPayTransactions (account, start, end, period) { } console.log ('---totals---'); let comSP = parseFloat(totalSp.toFixed(3))+parseFloat(curTotalSp.toFixed(3))+parseFloat(producerSPRewards.toFixed(3))+parseFloat(total_STEEM.toFixed(3)) - + parseFloat(authorRewardedSp.toFixed(3))+parseFloat(authorRewardedSTEEM.toFixed(3)); + + parseFloat(authorRewardedSp.toFixed(3))+parseFloat(authorRewardedSTEEM.toFixed(3)) + parseFloat(accountSTEEMTransferIn.toFixed(3)); console.log ('totalSTEEM:'+comSP.toFixed(3)); if (period>0 && comSP>0){ console.log ('AVG Daily:'+comSP/period); } //console.log ('totalSTEEM:'+totalSTEEM.toFixed(3)); - let comSBD = parseFloat(totalSBD.toFixed(3))+parseFloat(authorRewardedSBD.toFixed(3)); + let comSBD = parseFloat(totalSBD.toFixed(3))+parseFloat(authorRewardedSBD.toFixed(3))+ parseFloat(accountSBDTransferIn.toFixed(3));; console.log ('totalSBD:'+comSBD); if (period>0 && comSBD>0){ console.log ('AVG Daily:'+comSBD/period); From 5d67efd45d9234432b86fa1401ec97dd41a430e9 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 10 May 2019 02:19:58 +0300 Subject: [PATCH 091/193] Implement badge specific API Implement badge specific API --- app.js | 209 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 123 insertions(+), 86 deletions(-) diff --git a/app.js b/app.js index b81b789..1e196f4 100644 --- a/app.js +++ b/app.js @@ -232,6 +232,51 @@ app.get('/charities', async function (req, res) { res.send(charities); }); +/* end point for fetching user's current badges */ +app.get('/userBadges/:user', async function (req, res) { + let user = await db.collection('user_badges').find({user: req.params.user}).toArray(); + res.send(user); +}); + +/* claim this badge and store it for this user */ +app.get('/claimBadge/', async function (req, res) { + if (req.query.user && req.query.badge){ + let proceed = false; + //double check user eligibility in case of ISO + if (req.query.badge === 'iso'){ + let isoParticipant = await db.collection('iso_participants').find({user: req.query.user}).toArray(); + if (isoParticipant.length > 0){ + proceed = true; + } + } + if (proceed){ + let user_badge = { + user: req.query.user, + badge: req.query.badge, + date_claimed: new Date(), + }; + try{ + let transaction = await db.collection('user_badges').insert(user_badge); + console.log('success inserting post data'); + res.send({status: 'success', user: req.query.user, badge: req.query.badge}); + }catch(err){ + console.log('error'); + res.send({status: 'error'}); + } + }else{ + res.send({status: 'error'}); + } + }else{ + res.send({status: 'error'}); + } +}); + +/* end point for checking if user took part of ISO event */ +app.get('/isoParticipant/:user', async function (req, res) { + let user = await db.collection('iso_participants').find({user: req.params.user}).toArray(); + res.send(user); +}); + /* end point for returning current active delegator data by actifit */ app.get('/topDelegators', async function (req, res) { var delegatorList; @@ -512,6 +557,7 @@ app.get('/getRank/:user', async function (req, res) { console.log('already delegated'); delegSP = userDelegations.steem_power; } + //console.log('delegSP:'+delegSP); //console.log(userDelegations.steem_power); var delegation_score = 0; @@ -528,12 +574,15 @@ app.get('/getRank/:user', async function (req, res) { //also check the other case where the account is an alt-account delegator_info = await getAltAccountByNameFunc(req.params.user); //check if returned object is not empty - if (Object.keys(delegator_info).length > 0){ - if (parseInt(delegator_info.user_rank_benefit) == 1){ - //get original user delegation amount - userDelegations = await activeDelegationFunc(delegator_info.delegator); - if (userDelegations != null){ - delegSP += userDelegations.steem_power; + if (delegator_info.length > 0){ + for (let x=0, max_limit=delegator_info.length;x Date: Fri, 10 May 2019 02:20:32 +0300 Subject: [PATCH 092/193] Implement alt beneficiary status for SP rank Implement multi-account alt beneficiary status for SP rank From 0cdd05f80a79edfa7969c2af895464724841a2b9 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 10 May 2019 02:24:28 +0300 Subject: [PATCH 093/193] missing case over boundary activity count : 150k + Fix missing case of over boundary activity count : 150k + --- curation-bot.js | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 9764ea8..a62824a 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -41,13 +41,24 @@ var http = require("http"); setInterval(function() { try{ http.get("http://actifitvoter.herokuapp.com"); - //let's also run our token exchange cleanup process - console.log('running cleanup'); - request('https://actifitbot.herokuapp.com/cancelOutdatedAfitSteemExchange', function (error, response, body) { - console.log('cleanup result'); - console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received - console.log('error: '+ error) - }); + + if (!is_voting){ + //let's also run our token exchange cleanup process + console.log('running cleanup'); + request('https://actifitbot.herokuapp.com/cancelOutdatedAfitSteemExchange', function (error, response, body) { + console.log('cleanup result'); + console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received + console.log('error: '+ error) + }); + + //let's also run the cleanup for any missed AFIT SE to Actifit wallet processes + /*request('https://actifitbot.herokuapp.com/confirmAFITSEBulk', function (error, response, body) { + console.log('process any missed AFIT SE to Actifit Wallet'); + //console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received + //console.log('error: '+ error) + });*/ + } + }catch(err){ console.log('error:'+err); } @@ -63,7 +74,9 @@ const activity_rules = [ [7999,0.50], [8999,0.65], [9999,0.80], - [10000,1.00] + [10000,1.00], + [149999,1.00], + [150000,0], ] const content_rules = [ @@ -817,6 +830,9 @@ function processVotes(query, subsequent) { //calculate activity count score post.activity_score = utils.calcScore(activity_rules, config.activity_factor, post.json.step_count); + //console.log('step count:'+post.json.step_count); + //console.log('activity score:'+post.activity_score); + //skip post if it has less than min activity recorded if (post.activity_score == 0){ continue; From f7d1ef86ed52312325b987dfcd950107d9d9cf0c Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 20 May 2019 15:42:51 +0300 Subject: [PATCH 094/193] Support PromoCode & New Badges Implement support for promocode account creation Implement support for the rewarded activity report count badge type --- app.js | 166 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 120 insertions(+), 46 deletions(-) diff --git a/app.js b/app.js index 1e196f4..3b4096a 100644 --- a/app.js +++ b/app.js @@ -241,13 +241,42 @@ app.get('/userBadges/:user', async function (req, res) { /* claim this badge and store it for this user */ app.get('/claimBadge/', async function (req, res) { if (req.query.user && req.query.badge){ + const iso_badge = 'iso'; + const rew_activity_badge = 'rewarded_activity_lev_'; let proceed = false; //double check user eligibility in case of ISO - if (req.query.badge === 'iso'){ + if (req.query.badge === iso_badge){ let isoParticipant = await db.collection('iso_participants').find({user: req.query.user}).toArray(); if (isoParticipant.length > 0){ proceed = true; } + }else if (req.query.badge.includes(rew_activity_badge)){ + //double check user eligibility in case of Rewarded Activity + req.params.user = req.query.user; + let activityCount = await userRewardedPostCountFunc(req, res); + //console.log('activityCount:'+activityCount); + let badgeLevel = req.query.badge.replace(rew_activity_badge,''); + //console.log('badgeLevel:'+badgeLevel); + let rewarded_posts_rules = [ + [9,0], + [29,1], + [59,2], + [89,3], + [119,4], + [179,5], + [359,6], + [539,7], + [719,8], + [1079,9], + [1080,10] + ]; + rewarded_posts_rules.some(function (item){ + //console.log(item); + //if we are attempting to claim the proper activity count passing level at proper matching level, proceed + if (parseInt(activityCount) > item[0] && parseInt(badgeLevel) == item[1] + 1){ + proceed = true; + } + }); } if (proceed){ let user_badge = { @@ -557,7 +586,6 @@ app.get('/getRank/:user', async function (req, res) { console.log('already delegated'); delegSP = userDelegations.steem_power; } - //console.log('delegSP:'+delegSP); //console.log(userDelegations.steem_power); var delegation_score = 0; @@ -581,7 +609,6 @@ app.get('/getRank/:user', async function (req, res) { userDelegations = await activeDelegationFunc(delegator_info[x].delegator); if (userDelegations != null){ delegSP += userDelegations.steem_power; - //console.log('delegSP:'+delegSP); } } } @@ -865,7 +892,9 @@ proceedAccountCreation = async function (req){ transStored = await storeSignupTransaction(req); //proceed only if a proper referrer was sent if (typeof req.query.referrer != 'undefined' && req.query.referrer != 'undefined' && req.query.referrer != null){ - referralRewarded = await storeReferralReward(req); + if (!req.query.promo_proceed || (req.query.promo_proceed && req.query.referrer_reward)){ + referralRewarded = await storeReferralReward(req); + } } } console.log('account created:'+accountCreated); @@ -886,6 +915,9 @@ storeSignupTransaction = async function (req){ account_created: true, payment_confirmed: true, confirming_tx: req.query.confirming_tx, + promo_code: req.query.promo_code, + promo_used: req.query.promo_proceed, + signup_reward: req.query.signup_reward, date: new Date(), } @@ -914,32 +946,34 @@ storeSignupTransaction = async function (req){ } //also store this properly into user balance + if (!req.query.promo_proceed || (req.query.promo_proceed && req.query.signup_reward)){ - new_transaction = { - user: req.query.new_account, - reward_activity: 'Signup Reward', - token_count: parseFloat(req.query.afit_reward), - date: new Date(), - steem_invest: parseFloat(req.query.steem_invest), - usd_invest: parseFloat(req.query.usd_invest), - note: 'Successful Signup', - } - - //make sure we're not double rewarding user - query = { - user: req.query.new_account, - reward_activity: 'Signup Reward', - }; - - try{ - let transaction = db.collection('token_transactions') - .replaceOne(query, new_transaction, { upsert: true }); - result = true; - }catch(e){ - console.log(e); - result = false; + new_transaction = { + user: req.query.new_account, + reward_activity: 'Signup Reward', + token_count: parseFloat(req.query.afit_reward), + date: new Date(), + steem_invest: parseFloat(req.query.steem_invest), + usd_invest: parseFloat(req.query.usd_invest), + note: 'Successful Signup', + } + + //make sure we're not double rewarding user + query = { + user: req.query.new_account, + reward_activity: 'Signup Reward', + }; + + try{ + let transaction = db.collection('token_transactions') + .replaceOne(query, new_transaction, { upsert: true }); + result = true; + }catch(e){ + console.log(e); + result = false; + } + console.log(result); } - console.log(result); return result; } @@ -1145,34 +1179,74 @@ app.get('/confirmPayment', async function(req,res){ res.write(' '); }, 6000); try{ - //first step is to ensure memo has not been tampered with, nor has it been claimed before - //to do that, let's try to find if any signup has been done using this memo - let memo_used = await db.collection('signup_transactions').findOne({memo: req.query.memo}); - console.log('memo_used:'+memo_used); - if (typeof memo_used == "undefined" || memo_used == null){ - paymentReceivedTx = await utils.confirmPaymentReceived(req); - console.log('>>>> got TX '+paymentReceivedTx); - if (paymentReceivedTx != ''){ - req.query.confirming_tx = paymentReceivedTx; - console.log(req.query); + //check if promo code was sent, and confirm against available promo codes + if (req.query.promo_code){ + let promo_match = await db.collection('signup_promo_codes').findOne({code: req.query.promo_code}); + console.log(promo_match); + + if (promo_match && parseInt(promo_match.entries) > 0){ + //proceed creating account + req.query.promo_proceed = true; + req.query.signup_reward = promo_match.signup_reward; + req.query.referrer_reward = promo_match.referrer_reward; + try{ + accountCreated = await claimAndCreateAccount(req); + //only delegate if account created and delegation is enabled + if (accountCreated && promo_match.delegation){ + delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate); + } + + //decrease number of permitted entries + //update current user's token balance & store to db + promo_match.entries = parseInt(promo_match.entries) - 1; + console.log('promo_match.entries:'+promo_match.entries); try{ - accountCreated = await claimAndCreateAccount(req); - if (accountCreated){ - delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate); + let trans = await db.collection('signup_promo_codes').save(promo_match); + console.log('success updating pending entries'); + }catch(err){ + console.log(err); + return; + } + }catch(e){ + console.log(e); + } + paymentReceivedTx = req.query.promo_code; + } + res.write(JSON.stringify({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated})); + res.end(); + }else{ + req.query.promo_proceed = false; + + //first step is to ensure memo has not been tampered with, nor has it been claimed before + //to do that, let's try to find if any signup has been done using this memo + let memo_used = await db.collection('signup_transactions').findOne({memo: req.query.memo}); + console.log('memo_used:'+memo_used); + if (typeof memo_used == "undefined" || memo_used == null){ + paymentReceivedTx = await utils.confirmPaymentReceived(req); + console.log('>>>> got TX '+paymentReceivedTx); + if (paymentReceivedTx != ''){ + req.query.confirming_tx = paymentReceivedTx; + console.log(req.query); + try{ + accountCreated = await claimAndCreateAccount(req); + if (accountCreated){ + delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate); + } + }catch(e){ + console.log(e); } - }catch(e){ - console.log(e); } } + //res.send({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated}); + res.write(JSON.stringify({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated})); + res.end(); } }catch(err){ console.log(err); } //we're done, let's clear our running interval clearInterval(intID); - //res.send({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated}); - res.write(JSON.stringify({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated})); - res.end(); + } }); From 7ddaf2613eed7712f2a2a6b1a960c414f6f918fa Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 24 May 2019 15:17:53 +0300 Subject: [PATCH 095/193] Implement Lucky Winner API - Implement Lucky Winner API - Add new endpoint for all badges --- app.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app.js b/app.js index 3b4096a..4ab4fb0 100644 --- a/app.js +++ b/app.js @@ -238,11 +238,31 @@ app.get('/userBadges/:user', async function (req, res) { res.send(user); }); +/* end point for fetching all users badges */ +app.get('/allUserBadges/', async function (req, res) { + let badges = await db.collection('user_badges').find().toArray(); + let distinctUsers = [...new Set(badges.map(x => x.user))]; + res.send({badges: badges, userCount: distinctUsers.length}); +}); + +/* end point for fetching if the user had a random doubled up win before */ +app.get('/luckyWinner/:user', async function (req, res) { + let user = await db.collection('token_transactions').find({user: req.params.user, reward_activity : "Post", lucky_winner: 1}).toArray(); + res.send(user); +}); + +/* end point for fetching all random doubled up winners */ +app.get('/luckyWinnerList/', async function (req, res) { + let user = await db.collection('token_transactions').find({reward_activity : "Post", lucky_winner: 1}).toArray(); + res.send(user); +}); + /* claim this badge and store it for this user */ app.get('/claimBadge/', async function (req, res) { if (req.query.user && req.query.badge){ const iso_badge = 'iso'; const rew_activity_badge = 'rewarded_activity_lev_'; + const doubledup_badge = 'doubledup_badge'; let proceed = false; //double check user eligibility in case of ISO if (req.query.badge === iso_badge){ @@ -277,6 +297,11 @@ app.get('/claimBadge/', async function (req, res) { proceed = true; } }); + }else if (req.query.badge === doubledup_badge){ + let doubledupWinner = await db.collection('token_transactions').find({user: req.query.user, reward_activity : "Post", lucky_winner: 1}).toArray(); + if (doubledupWinner.length > 0){ + proceed = true; + } } if (proceed){ let user_badge = { From 16c522dab3e51413201f94e21a174539555d1cd8 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 8 Jun 2019 01:09:57 +0300 Subject: [PATCH 096/193] Implement API for charity donors Implement API for charity donors --- app.js | 12 ++++++++++++ utils.js | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 4ab4fb0..f74258f 100644 --- a/app.js +++ b/app.js @@ -251,6 +251,12 @@ app.get('/luckyWinner/:user', async function (req, res) { res.send(user); }); +/* end point for fetching if the user had contributed to charity before */ +app.get('/charityDonor/:user', async function (req, res) { + let user = await db.collection('token_transactions').find({reward_activity : "Charity Post", giver: req.params.user}).toArray(); + res.send(user); +}); + /* end point for fetching all random doubled up winners */ app.get('/luckyWinnerList/', async function (req, res) { let user = await db.collection('token_transactions').find({reward_activity : "Post", lucky_winner: 1}).toArray(); @@ -263,6 +269,7 @@ app.get('/claimBadge/', async function (req, res) { const iso_badge = 'iso'; const rew_activity_badge = 'rewarded_activity_lev_'; const doubledup_badge = 'doubledup_badge'; + const charity_badge = 'charity_badge'; let proceed = false; //double check user eligibility in case of ISO if (req.query.badge === iso_badge){ @@ -302,6 +309,11 @@ app.get('/claimBadge/', async function (req, res) { if (doubledupWinner.length > 0){ proceed = true; } + }else if (req.query.badge === charity_badge){ + let charityDonor = await db.collection('token_transactions').find({reward_activity : "Charity Post", giver: req.query.user}).toArray(); + if (charityDonor.length > 0){ + proceed = true; + } } if (proceed){ let user_badge = { diff --git a/utils.js b/utils.js index cf29d6b..af68106 100644 --- a/utils.js +++ b/utils.js @@ -4,7 +4,7 @@ var _ = require('lodash'); const axios = require('axios'); const dsteem = require('dsteem'); const moment = require('moment') -const steem_node = 'https://api.steemit.com';//'https://api.steem.house'; +const steem_node = 'https://api.steemit.com';//'https://steemd.minnowsupportproject.org';//'https://api.steem.house';// const client = new dsteem.Client(steem_node); var config; @@ -735,7 +735,7 @@ async function lookupAccountPay (){ const ONE_MONTH = 30; const ONE_YEAR = 365; - let start_days = 0; + let start_days = 1; let lookup_days = ONE_MONTH; let today = moment().utc().startOf('date').toDate() From 0d3846e52da9d5db38a647097e2ad7d6d21d3b2b Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 8 Jun 2019 01:10:21 +0300 Subject: [PATCH 097/193] Fix multi-delegator payment issue Fix multi-delegator payment issue --- delegations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delegations.js b/delegations.js index 7ab2c47..9b8ebf7 100644 --- a/delegations.js +++ b/delegations.js @@ -735,7 +735,7 @@ async function updateActiveDelegations () { function upsertRewardTransaction (reward) { return db.collection('token_transactions').update( - { user: reward.user, date: reward.date, reward_activity: reward.reward_activity }, + { user: reward.user, date: reward.date, reward_activity: reward.reward_activity, orig_account: reward.orig_account }, reward, { upsert: true } ) From 91e3afae874fee324af26ff6515deeace9395124 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:12:54 +0300 Subject: [PATCH 098/193] Implement Recurring fetching of AFITX balance - Implement Recurring fetching of AFITX balance - Implement functionality to store full & top AFITX holders --- app.js | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/app.js b/app.js index f74258f..6d95344 100644 --- a/app.js +++ b/app.js @@ -41,6 +41,25 @@ MongoClient.connect(url, function(err, client) { }); +let schedule = require('node-schedule') + +const SSC = require('sscjs'); +const ssc = new SSC(config.steem_engine_rpc); + +let rule = new schedule.RecurrenceRule(); + +let usersAFITXBal = []; +let fullSortedAFITXList = []; +//initial fetch +fetchAFITXBal(0); + +//fetch new AFITX user account balance every 5 mins +let scJob = schedule.scheduleJob('*/5 * * * *', async function(){ + //reset array + //usersAFITXBal = []; + fetchAFITXBal(0); +}); + //allows setting acceptable origins to be included across all function calls app.use(function(req, res, next) { var allowedOrigins = ['*', 'https://actifit.io', 'http://localhost:3000']; @@ -66,6 +85,50 @@ app.get('/', function (req, res) { }); +async function fetchAFITXBal(offset){ + try{ + console.log('--- Fetch new AFITX token balance ---'); + console.log(offset); + let tempArr = await ssc.find('tokens', 'balances', { symbol : 'AFITX' }, 1000, offset, '', false) //max amount, offset, + if (offset == 0 && tempArr.length > 0){ + console.log('>>Found new results, reset older ones'); + //reset existing data if we have fresh new data + usersAFITXBal = []; + } + usersAFITXBal = usersAFITXBal.concat(tempArr); + + if (tempArr.length > 999){ + //we possibly have more entries, let's call again + setTimeout(function(){ + fetchAFITXBal(usersAFITXBal.length); + }, 1000); + }else{ + //if we were not able to fetch entries, we need to try API again + if (offset == 0){ + console.log('no AFITX data, fetch again in 30 secs'); + setTimeout(function(){ + fetchAFITXBal(0); + }, 30000); + } + } + }catch(err){ + console.log(err); + if (offset == 0){ + console.log('no AFITX data, fetch again in 30 secs'); + setTimeout(function(){ + fetchAFITXBal(0); + }, 30000); + } + } + //console.log(usersAFITXBal); +} + +async function getAFITXUserData(user){ + let ind = fullSortedAFITXList.findIndex(v => v.account == user) + let entry = fullSortedAFITXList.find(v => v.account == user) + return {ind: ind, entry: entry} +} + /* function handles calculating and returning user token count */ grabUserTokensFunc = async function (username){ let user = await collection.findOne({_id: username}); From f4f57db83501b9a7a819845b52cccb21e9186cb5 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:14:24 +0300 Subject: [PATCH 099/193] Fix crash issue for latent db connection Implement fix for issue of fetching data from db when table data is not ready --- app.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 6d95344..9a890d1 100644 --- a/app.js +++ b/app.js @@ -192,10 +192,22 @@ app.get('/user-tokens-info', async function(req, res) { } } ]).toArray(function(err, results) { - var output = 'rewarded users:'+results[0].user_count+','; - output += 'tokens distributed:'+results[0].tokens_distributed; - res.send(results); - console.log(results); + if (results.length>0){ + try{ + var output = 'rewarded users:'+results[0].user_count+','; + output += 'tokens distributed:'+results[0].tokens_distributed; + res.send(results); + console.log(results); + }catch(err){ + console.log(err); + res.send(''); + } + }else{ + res.send(''); + } + }); + +}); }); }); From ecb562c5f56dd6c04965d45943a45fdcca9c856f Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:16:02 +0300 Subject: [PATCH 100/193] Create new endpoints AFIT+AFITX - Create new AFIT endpoint to return data in a better json formatted display - Create new AFITX endpoint to return list of top AFITX holders - Create new endpoint to fetch balance of single AFITX user --- app.js | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/app.js b/app.js index 9a890d1..79d2ffa 100644 --- a/app.js +++ b/app.js @@ -208,8 +208,49 @@ app.get('/user-tokens-info', async function(req, res) { }); }); + +/* end point for user total token count display */ +app.get('/topAFITHolders', async function (req, res) { + let tokenHolders = []; + if (isNaN(req.query.count)){ + tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).toArray(); + }else{ + tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); + } + res.send(tokenHolders); }); +/* end point for user total token count display */ +app.get('/topAFITXHolders', async function (req, res) { + let afitxSorted = utils.sortArrLodash(usersAFITXBal); + fullSortedAFITXList = afitxSorted; + let maxAmount = parseInt(req.query.count); + if (isNaN(maxAmount)){ + //set max as 100 + maxAmount = 100; + } + //always skip top holder as that would be actifit + afitxSorted = afitxSorted.slice(1, maxAmount + 1); + let output = afitxSorted; + if (req.query.pretty){ + output = '#|Token Holder | AFITX Tokens Held |
'; + output += '|---|---|---|
'; + for(var i = 0; i < afitxSorted.length; i++) { + let tokenHolder = afitxSorted[i]; + output += (i+1) + '|'; + output += '@'+tokenHolder.account + '|'; + output += gk_add_commas(parseFloat(tokenHolder.balance).toFixed(3)) + '|'; + output += '
'; + } + } + + res.send(output); +}); + +/* end point for fetching user AFITX data */ +app.get('/afitxData/:user', async function (req, res) { + let val = await getAFITXUserData(req.params.user); + res.send(val); }); From 4e1bb70b18275fbadf5d64aa52cdcb85f2f8724e Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:17:41 +0300 Subject: [PATCH 101/193] New endpoints for various data - New endpoint for fetching participants in actifit ISO - New endpoint for current product list under marketplace - New endpoint for list of professionals (trainers and nutrition experts) on actifit marketplace --- app.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app.js b/app.js index 79d2ffa..66c7aa1 100644 --- a/app.js +++ b/app.js @@ -459,6 +459,12 @@ app.get('/isoParticipant/:user', async function (req, res) { res.send(user); }); +/* end point for checking if user took part of ISO event */ +app.get('/isoParticipantList/', async function (req, res) { + let userList = await db.collection('iso_participants').find().toArray(); + res.send(userList); +}); + /* end point for returning current active delegator data by actifit */ app.get('/topDelegators', async function (req, res) { var delegatorList; @@ -501,6 +507,20 @@ app.get('/ambassadors', async function (req, res) { res.send(ambassadorList); }); +/* end point for returning current active professionals list */ +app.get('/professionals', async function (req, res) { + var professionalsList; + professionalsList = await db.collection('professionals').find({active:true}).sort({name: 1}).toArray(); + res.send(professionalsList); +}); + +/* end point for returning current active product list */ +app.get('/products', async function (req, res) { + var productsList; + productsList = await db.collection('products').find({active:true}).sort({name: -1}).toArray(); + res.send(productsList); +}); + /* end point for returning current top AFIT token holders */ app.get('/topTokenHolders', async function (req, res) { var tokenHolders; From e706c7d729dd41345e39d38891f655008bec60af Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:18:58 +0300 Subject: [PATCH 102/193] Implement backend tipping functionality Implement backend tipping functionality --- app.js | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/app.js b/app.js index 66c7aa1..4f611ce 100644 --- a/app.js +++ b/app.js @@ -552,6 +552,212 @@ app.get('/banned_users', async function (req, res) { }); +/* function handles the processing of a buy order */ +app.get('/tipAccount', async function(req, res){ + if (!req.query.user || !req.query.targetUser || !req.query.amount || !req.query.fundsPass) { + //make sure all params are sent + res.send({'error':'generic error'}); + }else{ + let user = req.query.user; + let targetUser = req.query.targetUser; + let amount = parseFloat(req.query.amount); + let fundsPass = req.query.fundsPass; + + + //check first if user is banned, as he wont be able to tip + let is_banned = await db.collection('banned_accounts').findOne({user: user, ban_status:"active"}); + if (is_banned){ + res.send({'error': 'You cannot tip AFIT as your account is banned'}); + return; + } + + //check first if targetUuser is banned, as he wont be able to tip + is_banned = await db.collection('banned_accounts').findOne({user: targetUser, ban_status:"active"}); + if (is_banned){ + res.send({'error': 'You cannot tip AFIT to a banned account'}); + return; + } + + //confirm matching funds password + let query = {user: user}; + + let entryFound = await db.collection('account_funds_pass').findOne(query, {fields : { _id:0} }); + + if (entryFound == null){ + res.send({'error': 'Account does not have a recorded funds password'}); + return; + }else if (!entryFound.passVerified){ + res.send({'error': 'Account\'s funds password not verified'}); + return; + }else{ + //create encrypted version of sent password + var cipher = crypto.createCipher(config.funds_encr_mode, config.funds_encr_key); + let encr_pass = cipher.update(fundsPass, 'utf8', 'hex'); + encr_pass += cipher.final('hex'); + if (entryFound.pass !== encr_pass){ + res.send({'error': 'Incorrect username and/or funds password'}); + return; + } + } + + //reached here, we're fine + + //confirm proper AFIT token balance. Test against target amount to be sent + let user_info = await grabUserTokensFunc (user); + console.log(user_info); + let cur_sender_token_count = parseFloat(user_info.tokens); + + if (cur_sender_token_count < amount){ + res.send({'error': 'Account does not have enough AFIT funds'}); + return; + } + + //check how much the user has tipped today + let totalTipAmount = await tippedToday(req, res); + if (parseFloat(totalTipAmount) >= parseFloat(config.max_allowed_tips_per_day)){ + res.send({'error': 'User cannot tip more today. Max tips per day is set at '+config.max_allowed_tips_per_day + ' AFIT'}); + return; + } + + if (parseFloat(totalTipAmount) + amount > parseFloat(config.max_allowed_tips_per_day)){ + res.send({'error': 'Tip amount exceeds daily limit (' + config.max_allowed_tips_per_day + ') by '+ ((parseFloat(totalTipAmount) + amount) - parseFloat(config.max_allowed_tips_per_day) ) + ' AFIT. Try a smaller amount.'}); + return; + } + + //perform transaction, decrease sender amount + let tipTrans = { + user: user, + reward_activity: 'Send Tip', + recipient: targetUser, + token_count: -amount, + tip_amount: amount, + note: user + ' tipped ' + targetUser + ' ' + amount + ' AFIT', + date: new Date(), + } + try{ + console.log(tipTrans); + let transaction = await db.collection('token_transactions').insert(tipTrans); + console.log('success inserting tip data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing tip action. DB storing issue'}); + return; + } + + //perform transaction, increase recipient amount + let tipReceiptTrans = { + user: targetUser, + reward_activity: 'Receive Tip', + sender: user, + token_count: amount, + tip_amount: amount, + note: user + ' tipped ' + targetUser + ' ' + amount + ' AFIT', + date: new Date(), + } + + try{ + console.log(tipReceiptTrans); + let transaction = await db.collection('token_transactions').insert(tipReceiptTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing tip action. DB storing issue'}); + return; + } + + + //update sending user's token balance & store to db + let new_token_count = cur_sender_token_count - amount; + user_info.tokens = new_token_count; + console.log('new_token_count:'+new_token_count); + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success updating user token count'); + }catch(err){ + console.log(err); + } + + //confirm proper AFIT token balance. Test against target amount to be sent + let target_user_info = await grabUserTokensFunc (targetUser); + if (target_user_info == null){ + //first time actifit user, let's create a new entry + target_user_info = new Object(); + target_user_info._id = targetUser; + target_user_info.user = targetUser; + target_user_info.tokens = 0; + } + console.log(target_user_info); + let cur_target_user_token_count = parseFloat(target_user_info.tokens); + + //update receiving user's token balance & store to db + let upd_token_count = cur_target_user_token_count + amount; + target_user_info.tokens = upd_token_count; + console.log('upd_token_count:'+upd_token_count); + try{ + let trans = await db.collection('user_tokens').save(target_user_info); + console.log('success updating user token count'); + }catch(err){ + console.log(err); + } + + res.send({'status': 'Success', 'tipAmount': amount,'senderTokenCount': new_token_count, 'recipientTokenCount': upd_token_count}); + } +}) + + +tippedToday = async function (req, res){ + let startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + //console.log("startDate:"+startDate+" endDate:"+endDate); + + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + + let endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + + query_json = { + "reward_activity": "Send Tip", + "date": { + "$lte": new Date(endDate), + "$gt": new Date(startDate) + } + }; + //adjust query to include user + //console.log(req.params.user) + if (req.params.user){ + query_json.user = req.params.user; + }else if (req.query.user){ + query_json.user = req.query.user; + } + + let result = await db.collection('token_transactions').find(query_json).toArray(); + let totalTipAmount = 0; + try{ + for (let i = 0; i< result.length; i++){ + //console.log(result[i]); + totalTipAmount += parseFloat(result[i].tip_amount); + } + console.log('totalTipAmount:'+totalTipAmount); + }catch(err){ + console.log(err.message); + } + return totalTipAmount; +} + +/* end point for counting amount of tips on a single day */ +app.get('/totalTipped', async function (req, res) { + let totalTipAmount = await tippedToday(req, res); + res.send(JSON.stringify({total_tip:totalTipAmount})); + +}); + +/* end point for counting amount of tips by user on a single day */ +app.get('/tippedToday/:user', async function (req, res) { + let totalTipAmount = await tippedToday(req, res); + res.send(JSON.stringify({total_tip:totalTipAmount})); + +}); + /* end point for counting number of reblogs on a certain date param (default current date) */ app.get('/reblogCount', async function (req, res) { var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); From 0e52e4fb2637b80f4014e49aa0a1c23d8ba68d66 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:20:18 +0300 Subject: [PATCH 103/193] Implement hard limit on trx max count Implement hard limit on trx max count returned when querying (1000) to prevent front and backend overload --- app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 4f611ce..cf44eaa 100644 --- a/app.js +++ b/app.js @@ -148,13 +148,13 @@ app.get('/user/:user', async function (req, res) { res.send(user); }); -/* end point for user transactions display (per user or general actifit token transactions, limited by 1000 */ +/* end point for user transactions display (per user or general actifit token transactions, limited by 1000) */ app.get('/transactions/:user?', async function (req, res) { let query = {}; var transactions; if(req.params.user){ query = {user: req.params.user} - transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); }else{ //only limit returned transactions in case this is a general query transactions = await db.collection('token_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); From b66c056213db8e6728704b1d96cdb07ca83fd067 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:22:08 +0300 Subject: [PATCH 104/193] Implement Core AFIT to SE Move Functionality Implement Core AFIT to SE Move Functionality Append new endpoint for checking if a user is banned --- app.js | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/app.js b/app.js index cf44eaa..579af58 100644 --- a/app.js +++ b/app.js @@ -551,6 +551,183 @@ app.get('/banned_users', async function (req, res) { res.send(banned_users); }); +/* end point for returning if a user is banned by actifit*/ +app.get('/is_banned/:user', async function (req, res) { + let is_banned = await db.collection('banned_accounts').findOne({user: req.params.user, ban_status:"active"}); + console.log (is_banned!=null) + res.send(is_banned!=null); +}); + +/* end point for returning if a user is powering down AFIT*/ +app.get('/isPoweringDown/:user', async function (req, res) { + let poweringDown = await db.collection('powering_down').findOne({user: req.params.user}); + console.log (poweringDown) + if (!poweringDown){ + res.send({}); + }else{ + res.send(poweringDown); + } +}); + +/* end point for returning the list of users powering down AFIT*/ +app.get('/poweringDownList/', async function (req, res) { + let poweringDown = await db.collection('powering_down').find().toArray(); + console.log (poweringDown) + res.send(poweringDown); +}); + +app.get('/cancelAFITMoveSE', async function(req, res){ + if (!req.query.user || !req.query.fundsPass){ + res.send({'error':'generic error'}); + }else{ + let user = req.query.user; + let fundsPass = req.query.fundsPass; + + //confirm matching funds password + let query = {user: user}; + + let entryFound = await db.collection('account_funds_pass').findOne(query, {fields : { _id:0} }); + + if (entryFound == null){ + res.send({'error': 'Account does not have a recorded funds password'}); + return; + }else if (!entryFound.passVerified){ + res.send({'error': 'Account\'s funds password not verified'}); + return; + }else{ + //create encrypted version of sent password + var cipher = crypto.createCipher(config.funds_encr_mode, config.funds_encr_key); + let encr_pass = cipher.update(fundsPass, 'utf8', 'hex'); + encr_pass += cipher.final('hex'); + if (entryFound.pass !== encr_pass){ + res.send({'error': 'Incorrect username and/or funds password'}); + return; + } + } + + //reached here, we're fine + + try{ + let result = await db.collection('powering_down').remove({user: req.query.user}); + res.send({'status': 'Success'}); + }catch(err){ + console.log(err); + } + } +}); + +/* function handles the processing of AFIT power down and moving tokens to S-E */ +app.get('/initiateAFITMoveSE', async function(req, res){ + if (!req.query.user || !req.query.amount || !req.query.fundsPass) { + //make sure all params are sent + res.send({'error':'generic error'}); + }else{ + let user = req.query.user; + let amount = parseFloat(req.query.amount); + let fundsPass = req.query.fundsPass; + + + //check first if user is banned, as he wont be able to move funds + let is_banned = await db.collection('banned_accounts').findOne({user: user, ban_status:"active"}); + if (is_banned){ + res.send({'error': 'You cannot move AFIT as your account is banned'}); + return; + } + + //check if amount is numeric + if (isNaN(amount)){ + res.send({'error': 'Amount sent is non numeric'}); + return; + } + + if (amount > config.max_afit_to_se_day){ + res.send({'error': 'You cannot transfer more than ' + config.max_afit_to_se_day + ' AFIT / day'}); + return; + } + + //confirm matching funds password + let query = {user: user}; + + let entryFound = await db.collection('account_funds_pass').findOne(query, {fields : { _id:0} }); + + if (entryFound == null){ + res.send({'error': 'Account does not have a recorded funds password'}); + return; + }else if (!entryFound.passVerified){ + res.send({'error': 'Account\'s funds password not verified'}); + return; + }else{ + //create encrypted version of sent password + var cipher = crypto.createCipher(config.funds_encr_mode, config.funds_encr_key); + let encr_pass = cipher.update(fundsPass, 'utf8', 'hex'); + encr_pass += cipher.final('hex'); + if (entryFound.pass !== encr_pass){ + res.send({'error': 'Incorrect username and/or funds password'}); + return; + } + } + + //reached here, we're fine + + //confirm proper AFIT token balance. Test against target amount to be sent + let user_info = await grabUserTokensFunc (user); + console.log(user_info); + let cur_sender_token_count = parseFloat(user_info.tokens); + + if (cur_sender_token_count < amount){ + res.send({'error': 'Account does not have enough AFIT funds'}); + return; + } + let afitx_se_balance = 0; + //confirm amount within AFITX conditions + let bal = await ssc.findOne('tokens', 'balances', { account: user, symbol: 'AFITX' }); + if (bal){ + afitx_se_balance = bal.balance; + }else{ + res.send({'error': 'Unable to fetch AFITX Funds. Try again later.'}); + return; + } + + //make sure user has at least 0.1 AFITX to move tokens + if (afitx_se_balance < 0.1){ + res.send({'error': 'You do not have enough AFITX to move AFIT tokens over.'}); + return; + } + //console.log(amount_to_powerdown); + //console.log(this.afitx_se_balance); + //calculate amount that can be transferred daily + if (amount / 100 > afitx_se_balance){ + res.send({'error': 'You do not have enough AFITX to move '+afitx_se_balance+ ' AFIT'}); + return; + } + + //register transaction for upcoming powering down cycle + + //query to see if user is already powering down + let tokenPowerDownQuery = { + user: user, + } + //store the transaction to the user's profile + let tokenPowerDownTrans = { + user: user, + daily_afit_transfer: amount, + min_afitx: amount / 100, + date: new Date(), + } + + try{ + console.log(tokenPowerDownTrans); + let transaction = await db.collection('powering_down').update(tokenPowerDownQuery, tokenPowerDownTrans, { upsert: true }); + console.log('success inserting power down data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing power down. DB storing issue'}); + return; + } + + res.send({'status': 'Success', trx: tokenPowerDownTrans}); + } +}) /* function handles the processing of a buy order */ app.get('/tipAccount', async function(req, res){ From 4eae115b05012a2ad4e688149b50e042179e8fa6 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:24:27 +0300 Subject: [PATCH 105/193] Adjust rank calculation to allow day delay factor Adjust rank calculation to allow day delay factor. Current factor is set at 2 days to provide enough time for users to get rewarded without being subjected or penalized by any voting round delays --- app.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 579af58..846bc30 100644 --- a/app.js +++ b/app.js @@ -1028,10 +1028,13 @@ userRewardedPostCountFunc = async function(req, res){ }; //if this is a sum for specific period v/s a total sum if (typeof req.query.period != "undefined" && !isNaN(req.query.period)){ - var days = req.query.period; + let days = req.query.period; //console.log("days:"+days); - var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - var endDate = moment(moment().utc().startOf('date').subtract(days, 'days').toDate()).format('YYYY-MM-DD'); + let startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (!isNaN(req.query.delay)){ + startDate = moment(moment().utc().startOf('date').subtract(req.query.delay, 'days').toDate()).format('YYYY-MM-DD'); + } + let endDate = moment(moment(startDate).utc().startOf('date').subtract(days, 'days').toDate()).format('YYYY-MM-DD'); //console.log("startDate:"+startDate+" endDate:"+endDate); //adjust query to include dates query_json = { @@ -1200,6 +1203,9 @@ app.get('/getRank/:user', async function (req, res) { //set the check period for config value of days days, and rerun the call to get last rewarded posting activity during this period req.query.period = config.recent_posts_period; + //add a 2 day delay to take into consideration late voting rounds + req.query.delay = 2; + var recent_rewarded_post_count = await userRewardedPostCountFunc(req, res); //console.log(recent_rewarded_post_count); From 1c2477575580fa6f5a6c95210db45576e3a5cddb Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:26:03 +0300 Subject: [PATCH 106/193] Implement AFITX Factor in Rank Calculation Implement AFITX Factor in Rank Calculation --- app.js | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 846bc30..40ad1b3 100644 --- a/app.js +++ b/app.js @@ -1213,8 +1213,24 @@ app.get('/getRank/:user', async function (req, res) { user_rank += recent_posts_score; + let rank_no_afitx = user_rank; + //also append AFITX based rank. for every 1 AFITX, increase 0.1 rank + let userHasAFITX = usersAFITXBal.find(entry => entry.account === req.params.user); + let user_rank_afitx = 0; + + if (userHasAFITX){ + user_rank_afitx = (parseFloat(userHasAFITX.balance) / 10).toFixed(2); + //max increase by holding AFITX is 100 + if (user_rank_afitx > 100){ + user_rank_afitx = 100; + } + user_rank += parseFloat(user_rank_afitx); + } + var score_components = JSON.stringify({ - user_rank: user_rank, + user_rank: user_rank.toFixed(2), + rank_no_afitx: rank_no_afitx, + afitx_rank: parseFloat(user_rank_afitx), delegation_score: delegation_score, afit_tokens_score: afit_tokens_score, tot_posts_score: tot_posts_score, @@ -1576,7 +1592,7 @@ storeReferralReward = async function (req){ app.get('/confirmAFITSEBulk', async function(req,res){ //let's call the service by S-E let url = new URL(config.steem_engine_trans_acct_his_lrg); - console.log(config.steem_engine_trans_acct_his_lrg); + //console.log(config.steem_engine_trans_acct_his_lrg); //connect with our service to confirm AFIT received to proper wallet try{ let se_connector = await fetch(url); @@ -1586,7 +1602,6 @@ app.get('/confirmAFITSEBulk', async function(req,res){ //console.log(trx_entries); trx_entries.forEach( async function(entry){ console.log(entry); - let user = entry.from; //query to see if entry already stored let tokenExchangeTransQuery = { From 80cb2abfa9a4cbc0cfa5805f719eca71ba2c8278 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:27:33 +0300 Subject: [PATCH 107/193] Improve Confirm AFIT SE Move Functionality Adjust Confirm AFIT SE Move Functionality so as entries do not get updated all the time, and can rely on prior transaction date --- app.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 40ad1b3..b7f1065 100644 --- a/app.js +++ b/app.js @@ -1614,7 +1614,7 @@ app.get('/confirmAFITSEBulk', async function(req,res){ reward_activity: 'Move AFIT SE to Actifit Wallet', token_count: parseFloat(entry.quantity), se_trx_ref: entry.txid, - date: new Date(), + date: new Date(entry.timestamp) } try{ console.log(tokenExchangeTrans); @@ -1672,6 +1672,7 @@ app.get('/confirmAFITSEReceipt', async function(req,res){ res.write(' '); }, 6000); let afit_amount = 0; + let found_entry = false; try{ //attempt to find matching transaction let targetUser = req.query.user; @@ -1679,6 +1680,7 @@ app.get('/confirmAFITSEReceipt', async function(req,res){ console.log(match_trx); //we found a match if (match_trx){ + found_entry = true; //query to see if entry already stored let tokenExchangeTransQuery = { user: targetUser, @@ -1690,7 +1692,7 @@ app.get('/confirmAFITSEReceipt', async function(req,res){ reward_activity: 'Move AFIT SE to Actifit Wallet', token_count: parseFloat(match_trx.quantity), se_trx_ref: match_trx.txid, - date: new Date(), + date: new Date(match_trx.timestamp) } try{ console.log(tokenExchangeTrans); @@ -1738,7 +1740,12 @@ app.get('/confirmAFITSEReceipt', async function(req,res){ //we're done, let's clear our running interval clearInterval(intID); //send response with confirming AFIT power up - res.write(JSON.stringify({'afit_se_power': 'success', 'afit_amount': afit_amount})); + let status = 'success'; + if (!found_entry){ + status = 'error'; + afit_amount = ''; + } + res.write(JSON.stringify({'afit_se_power': status, 'afit_amount': afit_amount})); res.end(); } }); From 17d75748d0c1c8878bf7e8efb6db0353088fef14 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:28:58 +0300 Subject: [PATCH 108/193] Implement Core Buy Product Functionality Implement Core Buy Product Functionality --- app.js | 436 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 436 insertions(+) diff --git a/app.js b/app.js index b7f1065..eab6f02 100644 --- a/app.js +++ b/app.js @@ -14,6 +14,8 @@ app.set('view engine', 'handlebars'); var config = utils.getConfig(); +let ObjectId = require('mongodb').ObjectId; + // Connection URL let url = config.mongo_uri; if (config.testing){ @@ -142,6 +144,22 @@ grabUserTokensFunc = async function (username){ return user; } +/* function handles returning product specific data */ +grabProductInfo = async function(product_id){ + let o_id = new ObjectId(product_id); + let product = await db.collection('products').findOne({_id: o_id}); + return product; +} + +/* function handles generating a random password/access_token */ +generatePassword = function (multip) { + //generate random 11 characters password + let passString = ''; + for (let i=0;i { + if (!err) { + console.log('match ebook'); + res.writeHead(200, { + "Content-Type": "application/pdf", + "Content-Disposition": "attachment; filename=" + fileName + }); + fs.createReadStream(filePath).pipe(res); + return; + } + console.log(err); + console.log('ebook does not exist'); + res.writeHead(400, {"Content-Type": "text/plain"}); + res.end("ERROR File does not exist"); + }); + +}) + + //function handles the process of confirming payment receipt, and then proceeds with account creation, reward and delegation app.get('/confirmPayment', async function(req,res){ if (req.query.confirm_payment_token != config.confirmPaymentToken){ @@ -2013,6 +2365,90 @@ app.get('/confirmPaymentPasswordVerify', async function(req,res){ }); +//function handles the process of confirming buy event for AFIT via STEEM +app.get('/confirmBuyAction', async function(req,res){ + let match_trx = ''; + let statusUpdated = false; + //keeping request alive to avoid timeouts + let intID = setInterval(function(){ + res.write(' '); + },8000); + try{ + match_trx = await utils.confirmPaymentReceivedBuy(req, config.signup_account); + console.log('>>>> got TX '+match_trx); + let targetUser = req.query.from; + if (match_trx != ''){ + try{ + //we found the transfer, now let's book proper AFIT tokens for the user + //query to see if entry already stored + let tokenBuyTransQuery = { + user: targetUser, + buy_trx_ref: match_trx + } + //store the transaction to the user's profile + let tokenBuyTrans = { + user: targetUser, + reward_activity: 'Buy AFIT Actifit.io', + steem_spent: parseFloat(req.query.steem_amount), + token_count: parseFloat(req.query.afit_amount), + buy_trx_ref: match_trx, + date: new Date(), + } + try{ + console.log(tokenBuyTrans); + //insert the query ensuring we do not write it twice + let transaction = await db.collection('token_transactions').update(tokenBuyTransQuery, tokenBuyTrans, { upsert: true }); + let trans_res = transaction.result; + console.log(trans_res); + /*console.log('nMatched:'+trans_res.nMatched); + console.log('nUpserted:'+trans_res.upserted); + console.log('nModified:'+trans_res.nModified);*/ + if (trans_res.upserted){ + //we have a new entry, increase user token count + + let user_info = await grabUserTokensFunc (targetUser); + + let cur_user_token_count = 0; + if (user_info){ + cur_user_token_count = parseFloat(user_info.tokens); + //update current user's token balance & store to db + let afit_amount = parseFloat(req.query.afit_amount); + let new_token_count = cur_user_token_count + parseFloat(afit_amount); + user_info.tokens = new_token_count; + console.log('new_token_count:'+new_token_count); + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success adding AFIT tokens to user balance'); + }catch(err){ + console.log(err); + return; + } + } + }else{ + //do nothing + } + }catch(err){ + console.log(err); + res.write(JSON.stringify({'error': 'Error adding AFIT tokens to user balance'})); + res.end(); + return; + } + }catch(e){ + console.log(e); + } + } + }catch(err){ + console.log(err); + } + //we're done, let's clear our running interval + clearInterval(intID); + //res.send({'paymentReceivedTx':paymentReceivedTx, 'accountCreated': accountCreated}); + res.write(JSON.stringify({'paymentReceivedTx': match_trx})); + res.end(); +}); + + + /* end point finding whether user has a pending AFIT token swap */ app.get('/userHasPendingTokenSwap/:user', async function(req, res){ let user_pending_swap = await db.collection('exchange_afit_steem').findOne({user: req.params.user,upvote_processed: {$in: [null, false, 'false']}},{fields : { _id:0} }); From 75ca5956ab3fddc89c6b7b3405b46f917f7d9f19 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:30:43 +0300 Subject: [PATCH 109/193] New Various Endpoints - New endpoint for fetching voting round data - New endpoint for fetching transaction list by type [and date] - Elaborate error message wording for AFIT to STEEM exchange --- app.js | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index eab6f02..93bc3e5 100644 --- a/app.js +++ b/app.js @@ -160,6 +160,13 @@ generatePassword = function (multip) { } return passString; }; + + +app.get('/votingStatus', async function (req, res) { + let votingStatus = await db.collection('voting_status').findOne({}); + res.send(votingStatus); +}); + /* end point for user total token count display */ app.get('/user/:user', async function (req, res) { let user = await grabUserTokensFunc(req.params.user); @@ -180,6 +187,42 @@ app.get('/transactions/:user?', async function (req, res) { res.send(transactions); }); +/* end point for transactions display by type (limited by 1000) */ +app.get('/transactionsByType/', async function (req, res) { + let query = {}; + let transactions = {}; + let proceed = false; + if (req.query.type){ + proceed = true; + query = {reward_activity: req.query.type} + + } + let startDate = ''; + let endDate = ''; + if (req.query.startDate){ + startDate = moment(moment(req.query.startDate).utc().startOf('date').add(1, 'days').toDate()).format('YYYY-MM-DD'); + } + if (req.query.endDate){ + //let endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + endDate = moment(moment(req.query.endDate).utc().endOf('date').add(1, 'days').toDate()).format('YYYY-MM-DD'); + } + if (startDate && endDate){ + query["date"] = { + "$lt": new Date(endDate), + "$gte": new Date(startDate) + }; + }else if (startDate){ + query["date"] = { + "$gte": new Date(startDate) + }; + } + console.log(query); + if (proceed){ + transactions = await db.collection('token_transactions').find(query).sort({date: 1}).limit(1000).toArray(); + } + res.send(transactions); +}); + /* end point for user referrals display (per user or general referrals */ app.get('/signups/:user?', async function (req, res) { let query = {account_created: true}; @@ -2540,7 +2583,7 @@ app.get('/performAfitSteemExchange', async function(req, res){ console.log(user_info); let cur_user_token_count = parseFloat(user_info.tokens); if (cur_user_token_count < config.min_afit_for_steem_upvote || cur_user_token_count < paid_tokens){ - res.send({'error': 'Account does not have enough AFIT funds'}); + res.send({'error': 'Account does not have enough AFIT funds in wallet. Minimum required is '+config.min_afit_for_steem_upvote}); return; } From c832083c8f484564790b07152c2a1a3736386b05 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:32:42 +0300 Subject: [PATCH 110/193] Wording improvements to actifit comments Wording improvements to actifit comments From 420b4b0bb3ae02bb3b4bb392f40f1fbc99260a83 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:34:23 +0300 Subject: [PATCH 111/193] Adjustments to actifit rewards comment Adjustments to actifit rewards comment --- comment.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/comment.md b/comment.md index cbf740e..f521445 100644 --- a/comment.md +++ b/comment.md @@ -1,15 +1,15 @@ Congrats on providing **Proof of Activity** via your Actifit report! {lucky_reward} You have accordingly been rewarded {token_count} AFIT tokens for your effort in reaching {step_count} activity, as well as your user rank and report quality! -You also received an {weight}% upvote via @actifit account. {exchange_vote} +You also received an {weight}% upvote via @actifit account. Actifit rewards and upvotes are based on your: -- User rank: which depends on your delegated SP, accumulated AFIT tokens, rewarded post count and recent rewarded activity. +- User rank: which depends on your delegated SP, accumulated AFIT tokens, rewarded post count, recent rewarded activity, and AFITX owned. - Post score: which depends on your activity count, post content, post upvotes, quality comments, moderator review and user rank. -To improve your user rank, delegate more, pile up more AFIT tokens, and post more. +To improve your user rank, delegate more, pile up more AFIT & AFITX tokens, and post more. To improve your post score, get to the max activity count, work on improving your post content, improve your user rank, engage with the community to get more upvotes and quality comments. -Actifit is now a Steem Witness. If you believe in our project, consider [voting for us](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=1) +Actifit is now a Steem Witness. If you believe in our project, consider [voting for us](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=true) ![rulersig2.jpg](https://cdn.steemitimages.com/DQmXrZz658YfMQBXNTA12rmbzqWXASfaGcNSqatJJ2ba7NR/rulersig2.jpg) Vote for [Actifit as a Witness](https://steemconnect.com/sign/account-witness-vote?witness=actifit&approve=1) From 5e00866f940cd25da932ea50837a1c9b1bf6c8a0 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:37:49 +0300 Subject: [PATCH 112/193] Implement multi-node + airdrop + AFIT 2 SE Implement multi-node approach to decrease server load Implement one time airdrop functionality Implement Recurring Move AFIT to SE functionality --- delegations.js | 285 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 283 insertions(+), 2 deletions(-) diff --git a/delegations.js b/delegations.js index 9b8ebf7..3aa2aab 100644 --- a/delegations.js +++ b/delegations.js @@ -31,7 +31,13 @@ let newestTxId = -1; console.log('--- Reward script initialized ---'); -var schedule = require('node-schedule') +let schedule = require('node-schedule') + +if (process.env.BOT_THREAD == 'MAIN'){ + + console.log('--- Main Bot Thread Detected ---'); + + //console.log('pre-schedule'); var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ console.log('--- Start delegators reward ---'); @@ -44,6 +50,281 @@ var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ runRewards(true); //runRewards(false); + +}else if (process.env.BOT_THREAD == 'SECOND_API'){ + + //let's schedule the AFIT to S-E token move event at 10:00 + let moveJob = schedule.scheduleJob({hour: 10, minute: 00}, function(){ + console.log('--- Start AFIT to S-E Move ---'); + moveAFITToSE(false);//param test + }); +}else{ + + runRewards(true); +} + +const SSC = require('sscjs'); +const ssc = new SSC(config.steem_engine_rpc); + +//airdropAFITX(); + +//moveAFITToSE(true); + +function moveAFITToSE(testMode){ + console.log('*** process moving AFIT to SE ***'); + let mongo_conn = config.mongo_uri + if (config.testing){ + mongo_conn = config.mongo_local + } + // Use connect method to connect to the server + MongoClient.connect(mongo_conn, async function (err, dbClient) { + if (!err) { + console.log('Connected successfully to server: ') + + db = dbClient.db(dbName) + // Get the documents collection + let poweringDown = await db.collection('powering_down').find().toArray(); + console.log (poweringDown) + + //sign key properly to function with dsteem requirement + let privateKey = dsteem.PrivateKey.fromString( + //config.token_dist_pkey + config.active_key + ); + + let delay = 0; + + //loop through entries, and send over AFIT + poweringDown.forEach(async function(entry){ + //let's make sure user still has proper AFITX amount + let userHasProperFunds = true; + let afitx_se_balance = 0; + let bal = await ssc.findOne('tokens', 'balances', { account: entry.user, symbol: 'AFITX' }); + if (bal){ + afitx_se_balance = bal.balance; + }else{ + console.log('error - Unable to fetch AFITX Funds. Try again later.'); + return; + } + //make sure user has at least 0.1 AFITX to move tokens + if (afitx_se_balance < 0.1){ + userHasProperFunds = false; + } + //console.log(amount_to_powerdown); + //console.log(this.afitx_se_balance); + //calculate amount that can be transferred daily + if (parseFloat(entry.daily_afit_transfer) / 100 > afitx_se_balance){ + userHasProperFunds = false; + } + + //make sure user has enough funds to send to SE + + let user = await db.collection('user_tokens').findOne({_id: entry.user}); + console.log(user); + //fixing token amount display for 3 digits + if (typeof user!= "undefined" && user!=null){ + if (typeof user.tokens!= "undefined"){ + user.tokens = user.tokens.toFixed(3) + }else{ + userHasProperFunds = false; + } + }else{ + userHasProperFunds = false; + } + let cur_user_token_count = 0; + try{ + cur_user_token_count = parseFloat(user.tokens); + if (cur_user_token_count < entry.daily_afit_transfer){ + userHasProperFunds = false; + } + }catch(err){ + userHasProperFunds = false; + } + + console.log('entry.user:'+entry.user+ ' afit bal:' + cur_user_token_count + ' bal:'+afitx_se_balance+' userHasProperFunds:'+userHasProperFunds); + if (userHasProperFunds){ + setTimeout(async function(){ + + try{ + + /*setTimeout(async function(){ + + + }, 1);*/ + + console.log(entry); + + let amount = parseFloat(entry.daily_afit_transfer); + + //perform transaction, decrease sender amount + let moveTrans = { + user: entry.user, + reward_activity: 'Move AFIT to S-E', + token_count: -amount, + note: 'User Automated transfer of ' + entry.daily_afit_transfer + ' AFIT to S-E', + date: new Date(), + } + + console.log(moveTrans); + //update our DB + if (!testMode){ + let transaction = await db.collection('token_transactions').insert(moveTrans); + } + console.log('success inserting move AFIT data'); + + //update user total token count + console.log('>>> update user token count'); + let user_info = await db.collection('user_tokens').findOne({_id: entry.user}); + let cur_sender_token_count = parseFloat(user_info.tokens); + let new_token_count = cur_sender_token_count - amount; + user_info.tokens = new_token_count; + console.log('user:' + entry.user + 'new_token_count:'+new_token_count); + if (!testMode){ + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success updating user token count'); + }catch(err){ + console.log(err); + } + } + + let json_data = { + contractName: 'tokens', + contractAction: 'transfer', + contractPayload: { + symbol: 'AFIT', + to: entry.user, + quantity: ''+entry.daily_afit_transfer,//needs to be string + memo: '' + } + } + + //broadcast to BC + console.log('broadcast to BC'); + if (!testMode){ + client.broadcast.json({ + required_auths: [config.account], + required_posting_auths: [], + id: 'ssc-mainnet1', + json: JSON.stringify(json_data), + }, privateKey).then( + result => { console.log(result) }, + error => { console.error(error) } + ) + } + + }catch(err){ + console.log(err); + console.log('error - Error inserting move AFIT data. DB storing issue'); + return; + } + + }, delay+=4500); + }else{ + console.log('error - user does not have enough funds'); + return; + } + }); + } else { + utils.log(err, 'delegations') + process.exit() + } + }) +} + +/* +//OUR AFITX AIRDROP FUNCTION +async function airdropAFITX(){ + let mongo_conn = config.mongo_uri + if (config.testing){ + mongo_conn = config.mongo_local + } + // Use connect method to connect to the server + MongoClient.connect(mongo_conn, async function (err, dbClient) { + if (!err) { + + console.log('Connected successfully to server: ') + db = dbClient.db(dbName) + + //let's fetch all users with proper AFIT count + let tokenHolders = await db.collection('user_tokens').find( { tokens: { $gte: 100 } }).toArray();//sort({tokens: -1}). + + //let's also fetch banned accounts, to ensure they dont receive an airdrop + let banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); + + let delay = 0; + + let totalAFITXSpent = 0; + let totalUsersRewarded = 0; + + //sign key properly to function with dsteem requirement + let privateKey = dsteem.PrivateKey.fromString( + //config.token_dist_pkey + config.active_key + ); + + tokenHolders.forEach(function(entry){ + //check if user is banned + //check if user is banned + let user_banned = false; + for (let n = 0; n < banned_users.length; n++) { + if (entry.user == banned_users[n].user){ + //console.log('User '+entry.user+' is banned, skipping' ); + user_banned = true; + break; + } + } + if (!user_banned){ + + setTimeout(function(){ + console.log(entry); + let rewardAFITX = 0; + let userAFIT = parseFloat(entry.tokens); + if (userAFIT >= 10000){ + rewardAFITX = 10; + }else if (userAFIT >= 100){ + rewardAFITX = (userAFIT/1000).toFixed(3); + } + let json_data = { + contractName: 'tokens', + contractAction: 'transfer', + contractPayload: { + symbol: 'AFITX', + to: entry.user, + quantity: '' + rewardAFITX,//needs to be string + memo: '' + } + } + console.log(json_data); + totalAFITXSpent += parseFloat(rewardAFITX); + totalUsersRewarded += 1; + client.broadcast.json({ + //required_auths: [config.token_dist_account], + required_auths: [config.account], + required_posting_auths: [], + id: 'ssc-mainnet1', + json: JSON.stringify(json_data), + }, privateKey).then( + result => { console.log(result) }, + error => { console.error(error) } + ) + //console.log('total airdrop:'+totalAFITXSpent); + //console.log('total recipients:'+totalUsersRewarded); + + }, delay+=3300); + } + }); + + + }else { + utils.log(err, 'delegations') + process.exit() + } + }); +} + +*/ + function runRewards(steemOnlyReward){ let mongo_conn = config.mongo_uri if (config.testing){ @@ -52,7 +333,7 @@ function runRewards(steemOnlyReward){ // Use connect method to connect to the server MongoClient.connect(mongo_conn, async function (err, dbClient) { if (!err) { - console.log('Connected successfully to server: ' + mongo_conn) + console.log('Connected successfully to server: ') db = dbClient.db(dbName) // Get the documents collection From db4e620db5439dbe252d6eead2c4baa7b012cc21 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:39:21 +0300 Subject: [PATCH 113/193] Implement timeout functionality w/ limited retries Implement timeout functionality w/ limited retries on confirmSEAFIT to prevent server errors --- utils.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/utils.js b/utils.js index af68106..6dde52c 100644 --- a/utils.js +++ b/utils.js @@ -114,8 +114,13 @@ var HOURS = 60 * 60; //function handles confirming if AFIT from SE were received async function confirmSEAFITReceived (targetUser) { getConfig(); + //track attempts for timeout + let attempts = 1; + let max_attempts = 15; return new Promise((resolve, reject) => { th_id = setInterval(async function(){ + if (attempts < max_attempts){ + attempts += 1; console.log('Check AFIT Power Up'); //let's call the service by S-E let url = new URL(config.steem_engine_trans_acct_his); @@ -142,6 +147,10 @@ var HOURS = 60 * 60; }catch(err){ console.log(err); } + }else{ + //return error + resolve(null); + } }, 5000); }); } From 8d26e98971be2299351d6132c983e4e04ccee9ce Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:40:33 +0300 Subject: [PATCH 114/193] Implement payment receipt confirmation Implement payment receipt confirmation functionality --- utils.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/utils.js b/utils.js index 6dde52c..dfa0f23 100644 --- a/utils.js +++ b/utils.js @@ -242,6 +242,52 @@ var HOURS = 60 * 60; }); } + //function handles confirming if payment was received + async function confirmPaymentReceivedBuy (req) { + getConfig(); + console.log('confirmPaymentReceivedBuy'); + return new Promise((resolve, reject) => { + let th_id = setInterval(async function(){ + console.log('check buy funds'); + steem.api.getAccountHistory(config.buy_account, -1, 800, (err, transactions) => { + let tx_id = ''; + let paymentFound = false; + for (let txs of transactions) { + let op = txs[1].op + //check if we received a transfer to our target account + //if we found a transfer operation sent to our target account, with the correct sender and the proper amount, proceed + if (op[0] === 'transfer'){ + let sentAmount = op[1].amount.split(' ')[0]; + if (op[1].to === config.buy_account && op[1].from === req.query.from && sentAmount === req.query.steem_amount){ + console.log('in'); + console.log(op[1]); + //console.log(txs); + + let now = moment(new Date()); //todays date + let end = moment(txs[1].timestamp); // last update date + let duration = moment.duration(now.diff(end)); + let hrs = duration.asHours(); + //transaction needs to have been concluded within 5 hours. + if (hrs < 5){ + tx_id = txs[1].trx_id; + paymentFound = true; + break; + } + } + } + } + if (paymentFound){ + //need to look again + console.log('found'); + clearInterval(th_id); + resolve(tx_id); + } + }); + }, 5000); + }); + } + + //function handles claiming spots for accounts async function claimDiscountedAccount(){ From 5790042df637393111a22a4d25d903ff4cf8cec8 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:41:33 +0300 Subject: [PATCH 115/193] Implement Easy Array Sort Functionality Implement Easy Array Sort Functionality to support top AFITX holders retrieval --- utils.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/utils.js b/utils.js index dfa0f23..0dd9f4a 100644 --- a/utils.js +++ b/utils.js @@ -1017,6 +1017,10 @@ async function steemPowerToVests (steemPower) { return parseFloat(steemPower * totalVests / totalSteem).toFixed(6); } +function sortArrLodash (arrToSort) { + return _.orderBy(arrToSort, function (o) { return new Number(o.balance)},['desc']); +} + module.exports = { updateSteemVariables: updateSteemVariables, @@ -1047,4 +1051,6 @@ async function steemPowerToVests (steemPower) { confirmPaymentReceived: confirmPaymentReceived, confirmPaymentReceivedPassword: confirmPaymentReceivedPassword, confirmSEAFITReceived: confirmSEAFITReceived, + confirmPaymentReceivedBuy: confirmPaymentReceivedBuy, + sortArrLodash: sortArrLodash, } From d507b679be5e4d821bb03af62e4bccc148b761b6 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:45:16 +0300 Subject: [PATCH 116/193] Implement AFITX Data & Prio Voting Implement AFITX Data & Prio Voting --- curation-bot.js | 191 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 179 insertions(+), 12 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index a62824a..1bed621 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -24,6 +24,10 @@ var skip = false; var version = '0.3.4'; var lucky_winner_id = -1; + +let usersAFITXBal = []; +let topUsersAFITX = []; + //version of the reward system var reward_sys_version = 'v0.2'; @@ -150,6 +154,27 @@ const actifit_img_urls = [ loadConfig(); var botNames; + +const SSC = require('sscjs'); +const ssc = new SSC(config.steem_engine_rpc); + + +// Initial Load of top AFITX token holders +// Top 25 will be stored in topUsersAFITX +fetchAFITXBal(0); + +setInterval(function(){ + try{ + if (!is_voting){ + // Load updated top AFITX token holders every 10 minutes + // Top 25 will be stored in topUsersAFITX + fetchAFITXBal(0); + } + }catch(err){ + console.log(err); + } +}, 1200000); // every 20 minutes (1200000) + steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true @@ -164,7 +189,7 @@ var url = config.mongo_uri; if (config.testing){ url = config.mongo_local; } -utils.log('db url:'+url); +//utils.log('db url:'+url); var db; var collection; @@ -302,7 +327,7 @@ let properties, rewardFund, rewardBalance, recentClaims, totalSteem, totalVests, async function startProcess() { if(!botNames) botNames = await utils.loadBots(); - if (config.detailed_logging) + //if (config.detailed_logging) utils.log('Start process'); // Load the settings from the config file each time so we can pick up any changes loadConfig(); @@ -338,16 +363,18 @@ async function startProcess() { //utils.updateSteemVariables(); + console.log('account'); + console.log(account!=null); + console.log('skip:'+skip); + console.log('is_voting:'+is_voting); + console.log('passedOneDay:'+passedOneDay); if (account && !skip && !is_voting && passedOneDay) { // Load the current voting power of the account var vp = utils.getVotingPower(account); - if (config.detailed_logging) - utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); + utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp*100))); - utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp))); - // We are at voting power kick start - time to vote! //utils.log(vp >= parseFloat(config.vp_kickstart)/100); if (vp >= parseFloat(config.vp_kickstart)/100 || config.testing) { @@ -504,6 +531,11 @@ function processVotes(query, subsequent) { utils.log('processVotes'); + //fetch top AFITX holders as of current + //top 25 will be stored in topUsersAFITX + //fetchAFITXBal(0); + + steem.api.getDiscussionsByCreated(query, async function (err, result) { //track how many queries were ran queryCount += 1; @@ -1232,20 +1264,106 @@ function processVotes(query, subsequent) { console.log('full_vote_value') console.log(full_vote_value) + console.log('topUsersAFITX') + console.log(topUsersAFITX); + + //number of found exchanges to perform in coming round + let matched_exchanges = 0; + //number of entries, maximum set by config + let list_length = Math.min(topUsersAFITX.length, config.topAFITXCount); + + + //loop through top AFITX holders to get confirmed upvotes + for (let xx=0 ; xx < list_length && matched_exchanges < config.max_afit_steem_upvotes_per_session ; xx++){ + let cur_top_entry = topUsersAFITX[xx]; + //find a matching report card, if exists + console.log(cur_top_entry.account); + let result = votePosts.find( user_post => user_post.author === cur_top_entry.account); + console.log('matching priority post'); + //console.log(result); + + //also find matching exchange request + + let cur_upvote_entry = afit_steem_upvote_list.find( upvote_request => upvote_request.user === cur_top_entry.account); + + //console.log('cur_upvote_entry'); + + //console.log(cur_upvote_entry); + + if (result && cur_upvote_entry){ + console.log('>>match found'); + matched_exchanges += 1; + //found a match, need to increase rewards according to AFIT pay + //calculate total paid AFIT in USD (which should be equal to a 65% reward, since Actifit removes 10% benefic, and author reward removes 75% + let usd_val_no_benef = parseFloat(cur_upvote_entry.paid_afit) * parseFloat(cur_afit_price.unit_price_usd); + + //expand the USD val to take into consideration 75% curation reward + let usd_val_no_curation = usd_val_no_benef * 0.75 / 0.65 + + //final upvote value after avoiding deductions + let usd_val = usd_val_no_benef / 0.65 + + //emulate proper voting power to give user matching rewards + let user_added_vote_weight = usd_val * 100 / full_vote_value; + + let entry_index = votePosts.findIndex( user_post => user_post.author === cur_upvote_entry.user); + + //decrease by 1% since assisting accounts will vote too (pay & funds) only if we still have room to use them + //only consume half of those now, leave the rest to other accounts + if (helping_accounts_votes < (config.max_helping_votes / 2)){ + user_added_vote_weight -= 1; + + helping_accounts_votes += 1; + + votePosts[entry_index].helperVotes = true; + } + + user_added_vote_weight = user_added_vote_weight.toFixed(2); + + votePosts[entry_index].additional_vote_weight = Math.floor(user_added_vote_weight * 100); + votePosts[entry_index].afit_swapped = cur_upvote_entry.paid_afit; + votePosts[entry_index].top_afitx_holder = 1; + console.log('Additional Vote Weight for AFIT/STEEM Upvote Exchange: '+votePosts[entry_index].author + ' ' + votePosts[entry_index].url); + console.log(votePosts[entry_index].additional_vote_weight); + + //we need to set params of this transaction + cur_upvote_entry.additional_vote_weight = votePosts[entry_index].additional_vote_weight / 100; + cur_upvote_entry.usd_val_no_benef = +usd_val_no_benef; + cur_upvote_entry.usd_val_no_curation = +usd_val_no_curation.toFixed(2); + cur_upvote_entry.usd_val = +usd_val.toFixed(2); + + cur_upvote_entry.reward_cycle_ID = reward_cycle_ID; + + cur_upvote_entry.top_afitx_holder = 1 + + cur_upvote_entry.post_author = votePosts[entry_index].author; + cur_upvote_entry.post_permlink = votePosts[entry_index].permlink; + + db.collection('exchange_afit_steem').save(cur_upvote_entry); + + //console.log(votePosts[entry_index]); + + } + } + console.log('afit_steem_upvote_list'); console.log(afit_steem_upvote_list); - let matched_exchanges = 0; - let list_length = afit_steem_upvote_list.length; + //number of entries + list_length = afit_steem_upvote_list.length; //loop through pending AFIT swaps, consuming older ones for (let xx=0 ; xx < list_length && matched_exchanges < config.max_afit_steem_upvotes_per_session ; xx++){ let cur_upvote_entry = afit_steem_upvote_list[xx]; //find a matching report card, if exists let result = votePosts.find( user_post => user_post.author === cur_upvote_entry.user); - console.log('matching post'); - console.log(result); + console.log('matching second post'); + //console.log(result); if (result != null){ + console.log('>>match found. Check if added already'); + //if this post has not been recorded yet + if (!result.additional_vote_weight){ + console.log('fresh game'); matched_exchanges += 1; //found a match, need to increase rewards according to AFIT pay //calculate total paid AFIT in USD (which should be equal to a 65% reward, since Actifit removes 10% benefic, and author reward removes 75% @@ -1290,11 +1408,11 @@ function processVotes(query, subsequent) { cur_upvote_entry.post_permlink = votePosts[entry_index].permlink; db.collection('exchange_afit_steem').save(cur_upvote_entry); - + } } } console.log('done going through pending AFIT to STEEM upvote exchange'); - + console.log('matched_exchanges:'+matched_exchanges); }catch(err){ utils.log(err); } @@ -1319,6 +1437,51 @@ function processVotes(query, subsequent) { }); } +async function fetchAFITXBal(offset){ + try{ + console.log('--- Fetch AFITX token balance --- '+offset); + console.log(offset); + let tempArr = await ssc.find('tokens', 'balances', { symbol : 'AFITX' }, 1000, offset, '', false) //max amount, offset, + if (offset == 0 && tempArr.length > 0){ + console.log('>>Found new results, reset older ones'); + //reset existing data if we have fresh new data + usersAFITXBal = []; + topUsersAFITX = []; + } + usersAFITXBal = usersAFITXBal.concat(tempArr); + if (tempArr.length > 999){ + //we possibly have more entries, let's call again + setTimeout(function(){ + fetchAFITXBal(usersAFITXBal.length); + }, 1000); + }else{ + //if we were not able to fetch entries, we need to try API again + if (offset == 0){ + console.log('no AFITX data, fetch again in 30 secs'); + setTimeout(function(){ + fetchAFITXBal(0); + }, 30000); + }else{ + console.log('AFITX list fetching complete'); + usersAFITXBal = utils.sortArrLodash(usersAFITXBal); + //skip first entry as thats Actifit account + topUsersAFITX = usersAFITXBal.slice(1, config.topAFITXCount); + + console.log(topUsersAFITX); + } + } + }catch(err){ + console.log(err); + if (offset == 0){ + console.log('no AFITX data, fetch again in 30 secs'); + setTimeout(function(){ + fetchAFITXBal(0); + }, 30000); + } + } + //console.log(usersAFITXBal); +} + var post_rank = 0; function votingProcess(posts, power_per_vote) { // Get the first bid in the list @@ -1563,6 +1726,10 @@ function sendComment(post, retries, vote_weight) { content = content.replace(/\{exchange_vote}/g,'') } + //if this is a top AFITX reward exchange + if (post.top_afitx_holder != null){ + jsonMetadata.top_afitx_holder = true; + } //if user is lucky winner, add a relevant message if (typeof post.lucky_winner != 'undefined' && post.lucky_winner != '' && post.lucky_winner != 'undefined'){ From 04cffdaace3164ef833ff67aaa79e9cb66716c70 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:46:50 +0300 Subject: [PATCH 117/193] Implement Voting API --- curation-bot.js | 53 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 1bed621..8ea9cc9 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -56,18 +56,19 @@ setInterval(function() { }); //let's also run the cleanup for any missed AFIT SE to Actifit wallet processes - /*request('https://actifitbot.herokuapp.com/confirmAFITSEBulk', function (error, response, body) { + request('https://actifitbot.herokuapp.com/confirmAFITSEBulk', function (error, response, body) { console.log('process any missed AFIT SE to Actifit Wallet'); + console.log(response); //console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received //console.log('error: '+ error) - });*/ + }); } }catch(err){ console.log('error:'+err); } -}, 600000); // every 10 minutes (600000) +}, 600000); // every 10 minutes (600000) var crypto = require('crypto'); @@ -273,6 +274,25 @@ let queryCount = 0; let properties, rewardFund, rewardBalance, recentClaims, totalSteem, totalVests, votePowerReserveRate, sbd_print_percentage; +async function setIsVoting(running){ + try{ + is_voting = running; + let votingStatus = await db.collection('voting_status').findOne({}); + if (!votingStatus){ + votingStatus = new Object(); + } + votingStatus.is_voting = running; + if (running){ + votingStatus.voting_start = new Date(); + }else{ + votingStatus.voting_end = new Date(); + } + db.collection('voting_status').save(votingStatus); + }catch(err){ + console.log(err); + } +} + //handles grabbing the vote value /STEEM function getVoteValue(voteWeight, account, currentVotingPower, steem_price) { @@ -540,7 +560,8 @@ function processVotes(query, subsequent) { //track how many queries were ran queryCount += 1; if (result && !err) { - is_voting = true; + //is_voting = true; + setIsVoting(true); utils.log(result.length + ' posts to process...'); @@ -555,6 +576,7 @@ function processVotes(query, subsequent) { for(var i = 0; i < result.length; i++) { + var post = result[i]; @@ -587,6 +609,7 @@ function processVotes(query, subsequent) { continue; } + //post.json_metadata = JSON.parse(body); // Check if any tags on this post are blacklisted in the settings @@ -673,6 +696,8 @@ function processVotes(query, subsequent) { try { + console.log('parsing data by post '+post.url); + post.json = JSON.parse(post.json_metadata); //we need to fetch the proper json_metadata from our own DB to ensure those have not been changed @@ -680,7 +705,7 @@ function processVotes(query, subsequent) { if (!config.testing){ let ver_url = config.api_url + "fetchVerifiedPost"; - let critical_fields = ['step_count', 'actiCrVal', 'actifitUserID']; + let critical_fields = ['step_count', 'actiCrVal', 'actifitUserID', 'activityDate']; let incons_detected = false; let incons_field = ''; @@ -693,9 +718,10 @@ function processVotes(query, subsequent) { } }); + //let's compare mission critical data to find if manipulation was done let auth_meta = verf_res.data.json_metadata; - + //console.log(auth_meta); //if either stored or current metadata is non-empty we need to investigate further if (auth_meta != '' || post.json_metadata != ''){ //check all critical values @@ -781,7 +807,7 @@ function processVotes(query, subsequent) { continue; } - + //console.log('still here'); //moving this section before the actual token rewards //due to the difference in server times, a user's post might have same date created. @@ -1432,6 +1458,10 @@ function processVotes(query, subsequent) { last_voted++; } else { utils.log(err, result); + //since we encountered an error, we need to restart the process + //is_voting = false; + setIsVoting(false); + votePosts = Array(); //errorEmail(err, config.report_emails); } }); @@ -1496,7 +1526,8 @@ function votingProcess(posts, power_per_vote) { utils.log('======================================================='); utils.log('Voting Complete!'); utils.log('======================================================='); - is_voting = false; + //is_voting = false; + setIsVoting(false); error_sent = false; saveState(); @@ -1517,6 +1548,7 @@ function sendVote(post, retries, power_per_vote) { var token_count = post.post_score;//parseFloat(post.rate_multiplier)*100; var vote_weight = Math.floor(post.rate_multiplier * power_per_vote); + let stdrd_vote_weight = vote_weight * config.partner_comm_vote_mult; console.log('vote weight:'+vote_weight); //if user had paid AFIT for extra STEEM upvotes, add this to their upvote value @@ -1536,6 +1568,11 @@ function sendVote(post, retries, power_per_vote) { if (vote_weight > config.max_vote_per_post){ vote_weight = config.max_vote_per_post; } + + if (stdrd_vote_weight > config.max_vote_per_post){ + stdrd_vote_weight = config.max_vote_per_post; + } + post.vote_weight = vote_weight; last_votes.push(post); From 3825c58e98495c69fdb708b78cc0ad232dacfe7c Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:47:33 +0300 Subject: [PATCH 118/193] Implement partner voting support Implement partner voting support for both ZZAN and SPORTSTALK using special dedicated actifit accounts --- curation-bot.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/curation-bot.js b/curation-bot.js index 8ea9cc9..6247103 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1630,6 +1630,34 @@ function sendVote(post, retries, power_per_vote) { utils.log(err); } } + + //if additional partner accounts enabled, vote using them as well + try{ + if (config.zzan_active){ + steem.broadcast.vote(config.zzan_pk, config.zzan_account, post.author, post.permlink, stdrd_vote_weight, function (err, result) { + utils.log('voting with '+config.zzan_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); + if (!err && result) { + utils.log(err, result); + } + }); + } + }catch(err){ + utils.log(err); + } + + try{ + if (config.sports_active){ + steem.broadcast.vote(config.sports_pk, config.sports_account, post.author, post.permlink, stdrd_vote_weight, function (err, result) { + utils.log('voting with '+config.sports_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); + if (!err && result) { + utils.log(err, result); + } + }); + } + }catch(err){ + utils.log(err); + } + steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { if (!err && result) { utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); From 1108703fea44a6f5422e30d0db135c91c00f88a8 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:49:52 +0300 Subject: [PATCH 119/193] Add support for yesterday post cases Add support for yesterday post functionality in detecting abusive (or unintended) double posts --- curation-bot.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 6247103..bdac511 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -818,9 +818,26 @@ function processVotes(query, subsequent) { let first_index = _.findIndex(votePosts, ['author', post.author]); let skip_date_diff = false; - if (last_index != -1 && (first_index!=last_index)) { - utils.log('---- User already has more than 2 posts in 24 hours ------'); + //if both posts have the same date using new json metadata format, definitely bail out + if (last_index != -1){ + utils.log('---- User has 2 posts, lets check if they have same target date ------'); let last_voted = votePosts[last_index]; + let json_meta_vals = JSON.parse(last_voted.json_metadata); + if ((typeof json_meta_vals.activityDate != 'undefined' && json_meta_vals.activityDate != '' && json_meta_vals.activityDate != 'undefined') && + (typeof post.json.activityDate != 'undefined' && post.json.activityDate != '' && post.json.activityDate != 'undefined')){ + //both posts have values and particularly same value + if (json_meta_vals.activityDate.length == post.json.activityDate.length + && json_meta_vals.activityDate.length > 0 + && json_meta_vals.activityDate[0] == post.json.activityDate[0]){ + utils.log('same target date with value '+ json_meta_vals.activityDate + ' ...skipping'); + //skip new post + skip_date_diff = true + }else{ + utils.log('posts okay different dates'); + } + }else{ + if (first_index!=last_index) { + utils.log('---- User already has more than 2 posts in 24 hours ------'); var last_date = moment(last_voted.created).format('D'); let first_voted = votePosts[first_index]; var first_date = moment(first_voted.created).format('D'); @@ -840,13 +857,11 @@ function processVotes(query, subsequent) { //skip new post skip_date_diff = true; - } - }else if (last_index != -1){ - utils.log('last_index:'+last_index); + }else{ + utils.log('reject a post if a prior one exists that is less than 6 hours away'); utils.log(post.author+post.url); //adding condition to reject a post if a prior one exists that is less than 6 hours away - let last_voted = votePosts[last_index]; //utils.log(last_voted.author+last_voted.url); var last_date = moment(last_voted.created).toDate(); var this_date = moment(post.created).toDate(); @@ -860,6 +875,8 @@ function processVotes(query, subsequent) { } } + } + } //store this entry to db to make sure we skip it in future voting iterations From 0b66c7b43d2b0e60b47247eee452c54cb56d765f Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 5 Sep 2019 15:51:24 +0300 Subject: [PATCH 120/193] Fix Crashing Issue + Remove Count Sorting Fix Crashing Issue due to skipped waiting for promise to complete Remove Count Sorting so as posts are processed on a FIFO basis --- curation-bot.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index bdac511..f7d82a6 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1200,7 +1200,7 @@ function processVotes(query, subsequent) { } try{ //award transaction tokens - bulk_transactions.execute(); + await bulk_transactions.execute(); }catch(bulkerr){ utils.log(bulkerr); } @@ -1225,10 +1225,14 @@ function processVotes(query, subsequent) { //if (!config.testing){ - votePosts.sort(function(post1, post2) { + /* //Sort posts by reverse score, so as when popping them we get sorted by highest + votePosts.sort(function(post1, post2) { + return post1.post_score - post2.post_score; }); + */ + //} From 08509686ef14e8b93edaa8c522fe2ea99fd9b953 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 6 Sep 2019 01:18:09 +0300 Subject: [PATCH 121/193] update outdated packages update outdated packages --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f21c7da..a941f46 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,11 @@ "description": "actifit curation bot", "main": "curation-bot.js", "dependencies": { - "axios": "^0.18.0", + "axios": "^0.18.1", "cheerio": "^1.0.0-rc.2", "dsteem": "^0.10.1", "express": "^4.16.2", - "jquery": "^3.3.1", + "jquery": "^3.4.1", "lodash": ">=4.17.11", "moment": "^2.22.2", "mongodb": "^3.0.10", @@ -17,6 +17,7 @@ "nodemailer-express-handlebars": "^3.0.0", "p-iteration": "^1.1.7", "request": "^2.88.0", + "sscjs": "0.0.8", "steem": "^0.6.7" }, "devDependencies": { From 7284e119ec6c816c2b237f8be028ddd88bf53918 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 12 Sep 2019 22:39:33 +0300 Subject: [PATCH 122/193] Append additional voting status params Append additional voting status params, including current main account VP + time till next round --- app.js | 25 ++++++++++++++++++-- utils.js | 70 +++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/app.js b/app.js index 93bc3e5..31a4bf8 100644 --- a/app.js +++ b/app.js @@ -28,6 +28,7 @@ var collection; const db_name = config.db_name; const collection_name = 'user_tokens'; + // Use connect method to connect to the server MongoClient.connect(url, function(err, client) { if(!err) { @@ -87,6 +88,16 @@ app.get('/', function (req, res) { }); +//initial load +let account = null; +loadAccountData(); + +async function loadAccountData(){ + //load main account data + + account = await utils.getAccountData(config.account); +} + async function fetchAFITXBal(offset){ try{ console.log('--- Fetch new AFITX token balance ---'); @@ -164,7 +175,14 @@ generatePassword = function (multip) { app.get('/votingStatus', async function (req, res) { let votingStatus = await db.collection('voting_status').findOne({}); - res.send(votingStatus); + if (!account){ + account = await utils.getAccountData(config.account); + } + let vp_res = await utils.getVotingPower(account); + + let reward_start = utils.toTimer(utils.timeTilKickOffVoting(vp_res * 100)); + + res.send({'status': votingStatus, 'vp': vp_res, 'reward_start': reward_start}); }); /* end point for user total token count display */ @@ -279,7 +297,7 @@ app.get('/topAFITHolders', async function (req, res) { tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); } res.send(tokenHolders); - }); +}); /* end point for user total token count display */ app.get('/topAFITXHolders', async function (req, res) { @@ -315,6 +333,7 @@ app.get('/afitxData/:user', async function (req, res) { }); + /* end point for returning total delegation payments (number of delegators and amount paid) on a specific date */ app.get('/delegationPayments', async function(req, res) { var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); @@ -996,6 +1015,8 @@ app.get('/tippedToday/:user', async function (req, res) { }); + + /* end point for counting number of reblogs on a certain date param (default current date) */ app.get('/reblogCount', async function (req, res) { var todayDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); diff --git a/utils.js b/utils.js index 0dd9f4a..12d8cd7 100644 --- a/utils.js +++ b/utils.js @@ -31,6 +31,18 @@ var HOURS = 60 * 60; let totalVests let totalSteem + async function getAccountData(account_name){ + let account = null; + //attempt to load account data + try{ + let account_res = await steem.api.getAccountsAsync([config.account]); + account = account_res[0]; + }catch(err){ + console.log(err); + } + return account; + } + function updateSteemVariables() { steem.api.getRewardFund("post", function (e, t) { console.log(e,t); @@ -78,7 +90,6 @@ var HOURS = 60 * 60; const currentManaPerc = currentMana * 100 / maxMana; - console.log(currentManaPerc); return currentManaPerc; } @@ -121,32 +132,32 @@ var HOURS = 60 * 60; th_id = setInterval(async function(){ if (attempts < max_attempts){ attempts += 1; - console.log('Check AFIT Power Up'); - //let's call the service by S-E - let url = new URL(config.steem_engine_trans_acct_his); - console.log(config.steem_engine_trans_acct_his); - //connect with our service to confirm AFIT received to proper wallet - try{ - let se_connector = await fetch(url); - let trx_entries = await se_connector.json(); - - let match_trx; - - //check if we have a proper entry matching user transfer - if (match_trx = trx_entries.find(trx => trx.from == targetUser)) { - //found match, let's make sure transaction is recent enough - console.log('found match'); - paymentFound = true; - if (paymentFound){ - //need to look again - console.log('found'); - clearInterval(th_id); - resolve(match_trx); + console.log('Check AFIT Power Up'); + //let's call the service by S-E + let url = new URL(config.steem_engine_trans_acct_his); + console.log(config.steem_engine_trans_acct_his); + //connect with our service to confirm AFIT received to proper wallet + try{ + let se_connector = await fetch(url); + let trx_entries = await se_connector.json(); + + let match_trx; + + //check if we have a proper entry matching user transfer + if (match_trx = trx_entries.find(trx => trx.from == targetUser)) { + //found match, let's make sure transaction is recent enough + console.log('found match'); + paymentFound = true; + if (paymentFound){ + //need to look again + console.log('found'); + clearInterval(th_id); + resolve(match_trx); + } } + }catch(err){ + console.log(err); } - }catch(err){ - console.log(err); - } }else{ //return error resolve(null); @@ -502,6 +513,10 @@ var HOURS = 60 * 60; function timeTilFullPower(cur_power){ return (STEEMIT_100_PERCENT - cur_power) * STEEMIT_VOTE_REGENERATION_SECONDS / STEEMIT_100_PERCENT; } + + function timeTilKickOffVoting(cur_power){ + return (config.vp_kickstart - cur_power) * STEEMIT_VOTE_REGENERATION_SECONDS / config.vp_kickstart; + } function getVestingShares(account) { var effective_vesting_shares = parseFloat(account.vesting_shares.replace(" VESTS", "")) @@ -790,7 +805,8 @@ async function lookupAccountPay (){ const ONE_MONTH = 30; const ONE_YEAR = 365; - let start_days = 1; + //when is our start day: 1 is yesterday, 10 is 10 days ago + let start_days = 11; let lookup_days = ONE_MONTH; let today = moment().utc().startOf('date').toDate() @@ -1030,6 +1046,7 @@ function sortArrLodash (arrToSort) { getVoteValueUSD: getVoteValueUSD, getVoteValue: getVoteValue, timeTilFullPower: timeTilFullPower, + timeTilKickOffVoting: timeTilKickOffVoting, getVestingShares: getVestingShares, loadUserList: loadUserList, getCurrency: getCurrency, @@ -1053,4 +1070,5 @@ function sortArrLodash (arrToSort) { confirmSEAFITReceived: confirmSEAFITReceived, confirmPaymentReceivedBuy: confirmPaymentReceivedBuy, sortArrLodash: sortArrLodash, + getAccountData: getAccountData, } From 37eb4642191329f582c9dd8310146ce13e411c2c Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 12 Sep 2019 22:40:41 +0300 Subject: [PATCH 123/193] Ensure user auto-moving AFIT is not banned Ensure user auto-moving AFIT is not banned --- delegations.js | 267 ++++++++++++++++++++++++++----------------------- 1 file changed, 143 insertions(+), 124 deletions(-) diff --git a/delegations.js b/delegations.js index 3aa2aab..0f8c672 100644 --- a/delegations.js +++ b/delegations.js @@ -38,18 +38,18 @@ if (process.env.BOT_THREAD == 'MAIN'){ console.log('--- Main Bot Thread Detected ---'); -//console.log('pre-schedule'); -var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ - console.log('--- Start delegators reward ---'); - runRewards(false);//param steemOnlyReward -}); - -//utils.lookupAccountPay(); + //console.log('pre-schedule'); + var j = schedule.scheduleJob({hour: 08, minute: 00}, function(){ + console.log('--- Start delegators reward ---'); + runRewards(false);//param steemOnlyReward + }); -//param steemOnlyReward -runRewards(true); -//runRewards(false); + //utils.lookupAccountPay(); + //param steemOnlyReward + runRewards(true); + //runRewards(false); + }else if (process.env.BOT_THREAD == 'SECOND_API'){ @@ -60,7 +60,7 @@ runRewards(true); }); }else{ - runRewards(true); + //runRewards(true); } const SSC = require('sscjs'); @@ -70,8 +70,9 @@ const ssc = new SSC(config.steem_engine_rpc); //moveAFITToSE(true); -function moveAFITToSE(testMode){ +async function moveAFITToSE(testMode){ console.log('*** process moving AFIT to SE ***'); + let mongo_conn = config.mongo_uri if (config.testing){ mongo_conn = config.mongo_local @@ -94,135 +95,153 @@ function moveAFITToSE(testMode){ let delay = 0; + //let's fetch banned accounts, to ensure they dont receive an airdrop + let banned_users = await db.collection('banned_accounts').find({ban_status:"active"}).toArray(); + //loop through entries, and send over AFIT poweringDown.forEach(async function(entry){ - //let's make sure user still has proper AFITX amount - let userHasProperFunds = true; - let afitx_se_balance = 0; - let bal = await ssc.findOne('tokens', 'balances', { account: entry.user, symbol: 'AFITX' }); - if (bal){ - afitx_se_balance = bal.balance; - }else{ - console.log('error - Unable to fetch AFITX Funds. Try again later.'); - return; - } - //make sure user has at least 0.1 AFITX to move tokens - if (afitx_se_balance < 0.1){ - userHasProperFunds = false; - } - //console.log(amount_to_powerdown); - //console.log(this.afitx_se_balance); - //calculate amount that can be transferred daily - if (parseFloat(entry.daily_afit_transfer) / 100 > afitx_se_balance){ - userHasProperFunds = false; - } - - //make sure user has enough funds to send to SE - let user = await db.collection('user_tokens').findOne({_id: entry.user}); - console.log(user); - //fixing token amount display for 3 digits - if (typeof user!= "undefined" && user!=null){ - if (typeof user.tokens!= "undefined"){ - user.tokens = user.tokens.toFixed(3) + //let's make sure user is not banned + let user_banned = false; + for (let n = 0; n < banned_users.length; n++) { + if (entry.user == banned_users[n].user){ + //console.log('User '+entry.user+' is banned, skipping' ); + user_banned = true; + break; + } + } + if (!user_banned){ + + //let's make sure user still has proper AFITX amount + let userHasProperFunds = true; + let afitx_se_balance = 0; + let bal = await ssc.findOne('tokens', 'balances', { account: entry.user, symbol: 'AFITX' }); + if (bal){ + afitx_se_balance = bal.balance; }else{ + console.log('error - Unable to fetch AFITX Funds. Try again later.'); + return; + } + //make sure user has at least 0.1 AFITX to move tokens + if (afitx_se_balance < 0.1){ userHasProperFunds = false; } - }else{ - userHasProperFunds = false; - } - let cur_user_token_count = 0; - try{ - cur_user_token_count = parseFloat(user.tokens); - if (cur_user_token_count < entry.daily_afit_transfer){ + //console.log(amount_to_powerdown); + //console.log(this.afitx_se_balance); + //calculate amount that can be transferred daily + if (parseFloat(entry.daily_afit_transfer) / 100 > afitx_se_balance){ userHasProperFunds = false; } - }catch(err){ - userHasProperFunds = false; - } - - console.log('entry.user:'+entry.user+ ' afit bal:' + cur_user_token_count + ' bal:'+afitx_se_balance+' userHasProperFunds:'+userHasProperFunds); - if (userHasProperFunds){ - setTimeout(async function(){ - - try{ + + //make sure user has enough funds to send to SE + + let user = await db.collection('user_tokens').findOne({_id: entry.user}); + console.log(user); + //fixing token amount display for 3 digits + if (typeof user!= "undefined" && user!=null){ + if (typeof user.tokens!= "undefined"){ + user.tokens = user.tokens.toFixed(3) + }else{ + userHasProperFunds = false; + } + }else{ + userHasProperFunds = false; + } + let cur_user_token_count = 0; + try{ + cur_user_token_count = parseFloat(user.tokens); + if (cur_user_token_count < entry.daily_afit_transfer){ + userHasProperFunds = false; + } + }catch(err){ + userHasProperFunds = false; + } + + console.log('entry.user:'+entry.user+ ' afit bal:' + cur_user_token_count + ' bal:'+afitx_se_balance+' userHasProperFunds:'+userHasProperFunds); + if (userHasProperFunds){ + setTimeout(async function(){ + + try{ + + /*setTimeout(async function(){ + + + }, 1);*/ - /*setTimeout(async function(){ + console.log(entry); + + let amount = parseFloat(entry.daily_afit_transfer); + //perform transaction, decrease sender amount + let moveTrans = { + user: entry.user, + reward_activity: 'Move AFIT to S-E', + token_count: -amount, + note: 'User Automated transfer of ' + entry.daily_afit_transfer + ' AFIT to S-E', + date: new Date(), + } - }, 1);*/ - - console.log(entry); - - let amount = parseFloat(entry.daily_afit_transfer); - - //perform transaction, decrease sender amount - let moveTrans = { - user: entry.user, - reward_activity: 'Move AFIT to S-E', - token_count: -amount, - note: 'User Automated transfer of ' + entry.daily_afit_transfer + ' AFIT to S-E', - date: new Date(), - } - - console.log(moveTrans); - //update our DB - if (!testMode){ - let transaction = await db.collection('token_transactions').insert(moveTrans); - } - console.log('success inserting move AFIT data'); - - //update user total token count - console.log('>>> update user token count'); - let user_info = await db.collection('user_tokens').findOne({_id: entry.user}); - let cur_sender_token_count = parseFloat(user_info.tokens); - let new_token_count = cur_sender_token_count - amount; - user_info.tokens = new_token_count; - console.log('user:' + entry.user + 'new_token_count:'+new_token_count); - if (!testMode){ - try{ - let trans = await db.collection('user_tokens').save(user_info); - console.log('success updating user token count'); - }catch(err){ - console.log(err); + console.log(moveTrans); + //update our DB + if (!testMode){ + let transaction = await db.collection('token_transactions').insert(moveTrans); } - } - - let json_data = { - contractName: 'tokens', - contractAction: 'transfer', - contractPayload: { - symbol: 'AFIT', - to: entry.user, - quantity: ''+entry.daily_afit_transfer,//needs to be string - memo: '' + console.log('success inserting move AFIT data'); + + //update user total token count + console.log('>>> update user token count'); + let user_info = await db.collection('user_tokens').findOne({_id: entry.user}); + let cur_sender_token_count = parseFloat(user_info.tokens); + let new_token_count = cur_sender_token_count - amount; + user_info.tokens = new_token_count; + console.log('user:' + entry.user + 'new_token_count:'+new_token_count); + if (!testMode){ + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success updating user token count'); + }catch(err){ + console.log(err); + } + } + + let json_data = { + contractName: 'tokens', + contractAction: 'transfer', + contractPayload: { + symbol: 'AFIT', + to: entry.user, + quantity: ''+entry.daily_afit_transfer,//needs to be string + memo: '' + } + } + + //broadcast to BC + console.log('broadcast to BC'); + if (!testMode){ + client.broadcast.json({ + required_auths: [config.account], + required_posting_auths: [], + id: 'ssc-mainnet1', + json: JSON.stringify(json_data), + }, privateKey).then( + result => { console.log(result) }, + error => { console.error(error) } + ) } - } - //broadcast to BC - console.log('broadcast to BC'); - if (!testMode){ - client.broadcast.json({ - required_auths: [config.account], - required_posting_auths: [], - id: 'ssc-mainnet1', - json: JSON.stringify(json_data), - }, privateKey).then( - result => { console.log(result) }, - error => { console.error(error) } - ) + }catch(err){ + console.log(err); + console.log('error - Error inserting move AFIT data. DB storing issue'); + return; } - }catch(err){ - console.log(err); - console.log('error - Error inserting move AFIT data. DB storing issue'); - return; - } - - }, delay+=4500); + }, delay+=4500); + }else{ + console.log('error - user does not have enough funds'); + return; + } }else{ - console.log('error - user does not have enough funds'); - return; + console.log('user ' + entry.user + ' is banned. Skip'); } }); } else { From c9f2a471df99d1a7b9aa6c52919c2e36047e70f3 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 13 Sep 2019 01:15:13 +0300 Subject: [PATCH 124/193] Fix issue calculating proper waiting time Fix issue calculating proper waiting time --- utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils.js b/utils.js index 12d8cd7..b2ea7c3 100644 --- a/utils.js +++ b/utils.js @@ -515,7 +515,7 @@ function timeTilFullPower(cur_power){ } function timeTilKickOffVoting(cur_power){ - return (config.vp_kickstart - cur_power) * STEEMIT_VOTE_REGENERATION_SECONDS / config.vp_kickstart; + return (parseInt(config.vp_kickstart) - cur_power) * STEEMIT_VOTE_REGENERATION_SECONDS / parseInt(config.vp_kickstart); } function getVestingShares(account) { From 691a800ccf9802f77dd6fd5edcf73bd981e2e4f4 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 Nov 2019 17:56:06 +0200 Subject: [PATCH 125/193] Implement Moderator Action End Point Implement Moderator Action End Point --- app.js | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) diff --git a/app.js b/app.js index 31a4bf8..38e0d6a 100644 --- a/app.js +++ b/app.js @@ -288,6 +288,151 @@ app.get('/user-tokens-info', async function(req, res) { }); +/* end point for user total token count display */ +app.get('/modAction', async function (req, res) { + if (!req.query.moderator || !req.query.fundsPass || !req.query.targetAction){ + res.send({'error':'Missing Data'}); + }else{ + let moderator = req.query.moderator; + let fundsPass = req.query.fundsPass; + + //confirm matching funds password + let query = {user: moderator}; + + if (!isModerator(moderator)){ + res.send({'error': 'Account does not have proper privileges'}); + return; + } + + let entryFound = await db.collection('account_funds_pass').findOne(query, {fields : { _id:0} }); + + if (entryFound == null){ + res.send({'error': 'Account does not have a recorded funds password'}); + return; + }else if (!entryFound.passVerified){ + res.send({'error': 'Account\'s funds password not verified'}); + return; + }else{ + //create encrypted version of sent password + var cipher = crypto.createCipher(config.funds_encr_mode, config.funds_encr_key); + let encr_pass = cipher.update(fundsPass, 'utf8', 'hex'); + encr_pass += cipher.final('hex'); + if (entryFound.pass !== encr_pass){ + res.send({'error': 'Incorrect username and/or funds password'}); + return; + } + } + + //reached here, we're fine + console.log('reached here, we\'re fine'); + + let result = ''; + + //store every moderator transaction as log + let modTrans = { + "moderator": req.query.moderator, + "action": req.query.targetAction, + "date": new Date(), + }; + + switch(req.query.targetAction){ + + case 'ban': + modTrans.user = req.query.banuser.trim().toLowerCase(); + collection = db.collection('banned_accounts') + //var dt = new Date().toJSON() + //dt.substring(0,dt.indexOf(".")); + + if (modTrans.user == ''){ + res.send({'error': 'Cannot ban empty user'}); + return; + } + result = await collection.insert({ + "user": req.query.banuser.trim().toLowerCase(), + "ban_date": new Date(), + "ban_length": req.query.ban_length, + "ban_status": 'active', + "ban_reason": req.query.ban_reason + }); + console.log(req.query.banuser+" banned "); + result.status='success'; + break; + case 'unban': + + modTrans.user = req.query.unbanuser.trim().toLowerCase(); + collection = db.collection('banned_accounts') + + if (modTrans.user == ''){ + res.send({'error': 'Cannot unban empty user'}); + return; + } + + result = await collection.update( + { "user": req.query.unbanuser.trim().toLowerCase() }, + { + $set: { + "ban_status": "inactive", + } + }, + { + multi: true + } + ); + console.log(req.query.unbanuser+" ban removed! "); + result.status='success'; + break; + + case 'resetpass': + modTrans.user = req.query.resetuser.trim().toLowerCase(); + + collection = db.collection('account_funds_pass') + + if (modTrans.user == ''){ + res.send({'error': 'Cannot resetpass empty user'}); + return; + } + + let user = req.query.resetuser.trim().toLowerCase(); + result = 'no change'; + if (user!=''){ + result = await collection.remove({ + "user": user, + }); + console.log(user+" password reset "); + } + console.log(user+" pass reset! "); + result.status='success'; + break; + + case 'reward': + modTrans.fullurl = req.query.fullurl.trim().toLowerCase(); + modTrans.vp = req.query.power; + if (modTrans.fullurl == ''){ + res.send({'error': 'Need to send URL to vote'}); + return; + } + + if (isNaN(modTrans.vp)){ + res.send({'error': 'VP needs to be numeric'}); + return; + } + result = await utils.rewardPost(modTrans.fullurl, modTrans.vp) + console.log(result); + result.status='success'; + break; + } + + collection = db.collection('team_transactions'); + let modTransRes = await collection.insert(modTrans); + console.log(modTransRes) + + res.send(result); + } +}); + + + + /* end point for user total token count display */ app.get('/topAFITHolders', async function (req, res) { let tokenHolders = []; From c6f54d630ce9f236885bc5faa9fa4bc1af02a5ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2019 15:56:34 +0000 Subject: [PATCH 126/193] Bump axios from 0.18.0 to 0.18.1 Bumps [axios](https://github.com/axios/axios) from 0.18.0 to 0.18.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v0.18.1/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v0.18.0...v0.18.1) Signed-off-by: dependabot[bot] --- package-lock.json | 1823 ++++++++++----------------------------------- 1 file changed, 376 insertions(+), 1447 deletions(-) diff --git a/package-lock.json b/package-lock.json index fcce6df..3b45fc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,11 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@types/node": { + "version": "12.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.7.tgz", + "integrity": "sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==" + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -19,42 +24,22 @@ "negotiator": "0.6.1" } }, - "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", - "dev": true - }, - "acorn-jsx": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", - "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", - "dev": true, - "requires": { - "acorn": "^5.0.3" - } - }, "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -75,12 +60,6 @@ "string-width": "^2.0.0" } }, - "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", - "dev": true - }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -106,15 +85,6 @@ "normalize-path": "^2.1.1" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -138,42 +108,24 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } }, "assert-plus": { "version": "1.0.0", @@ -219,69 +171,23 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "axios": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", - "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", "requires": { - "follow-redirects": "^1.3.0", - "is-buffer": "^1.1.5" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" }, "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" } } }, @@ -365,10 +271,9 @@ "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" }, "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "optional": true, + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "requires": { "tweetnacl": "^0.14.3" } @@ -385,9 +290,12 @@ "dev": true }, "bindings": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", - "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } }, "bip66": { "version": "1.1.5", @@ -424,6 +332,11 @@ "type-is": "~1.6.15" } }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -530,12 +443,6 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, "bytebuffer": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", @@ -566,21 +473,6 @@ "unset-value": "^1.0.0" } }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -619,11 +511,18 @@ "supports-color": "^5.3.0" } }, - "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", - "dev": true + "cheerio": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", + "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.1", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + } }, "chokidar": { "version": "2.0.4", @@ -661,12 +560,6 @@ "safe-buffer": "^5.0.1" } }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -696,21 +589,6 @@ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", @@ -730,11 +608,6 @@ } } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -761,9 +634,9 @@ "dev": true }, "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" } @@ -793,12 +666,6 @@ "xdg-basedir": "^3.0.0" } }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -826,9 +693,9 @@ "dev": true }, "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" }, "core-util-is": { "version": "1.0.2", @@ -911,6 +778,22 @@ "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", "dev": true }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -945,12 +828,6 @@ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, "define-properties": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", @@ -1007,29 +884,6 @@ } } }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1050,13 +904,35 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.3.tgz", "integrity": "sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc=" }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "requires": { - "esutils": "^2.0.2" + "dom-serializer": "0", + "domelementtype": "1" } }, "dot-prop": { @@ -1079,9 +955,9 @@ } }, "dsteem": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/dsteem/-/dsteem-0.9.0.tgz", - "integrity": "sha512-00t8Bekwfd4459Pjn0fcnb1LY8IT7Q+WGM0kJsI+CKWqOXP4xH/YuXwk7IPhDIo7la7Wskav6WmeVxFEutxq+w==", + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/dsteem/-/dsteem-0.10.1.tgz", + "integrity": "sha512-IF8yMagK+id3qHABEmQSrn7FjNiCTZzbohzig8jE09TBUpwePBxx+1UUedms/EY4oUlziaDa6h7Znb/Pp2dXgQ==", "requires": { "bs58": "^4.0.1", "bytebuffer": "^5.0.1", @@ -1093,9 +969,9 @@ }, "dependencies": { "node-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", - "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" } } }, @@ -1112,12 +988,12 @@ "dev": true }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "requires": { - "jsbn": "~0.1.0" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "ecurve": { @@ -1135,9 +1011,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -1161,38 +1037,10 @@ "iconv-lite": "~0.4.13" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz", - "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" - } - }, - "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", - "dev": true, - "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" - } + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, "escape-html": { "version": "1.0.3", @@ -1205,283 +1053,6 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, - "eslint": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.2.0.tgz", - "integrity": "sha512-zlggW1qp7/TBjwLfouRoY7eWXrXwJZFqCdIxxh0/LVB/QuuKuIMkzyUZEcDo6LBadsry5JcEMxIqd3H/66CXVg==", - "dev": true, - "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^3.1.0", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.2", - "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.5", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^1.1.0", - "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "string.prototype.matchall": "^2.0.0", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^4.0.3", - "text-table": "^0.2.0" - }, - "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "eslint-config-standard": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-11.0.0.tgz", - "integrity": "sha512-oDdENzpViEe5fwuRCWla7AXQd++/oyIp8zP+iP9jiUPG6NBj3SHgdgtl/kTn00AjeN+1HNvavTKmYbMo+xMOlw==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - } - }, - "eslint-module-utils": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz", - "integrity": "sha1-snA2LNiLGkitMIl2zn+lTphBF0Y=", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^1.0.0" - } - }, - "eslint-plugin-es": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.3.1.tgz", - "integrity": "sha512-9XcVyZiQRVeFjqHw8qHNDAZcQLqaHlOGGpeYqzYh8S4JYCWTCO3yzyen8yVmA5PratfzTRWDwCOFphtDEG+w/w==", - "dev": true, - "requires": { - "eslint-utils": "^1.3.0", - "regexpp": "^2.0.0" - }, - "dependencies": { - "regexpp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.0.tgz", - "integrity": "sha512-g2FAVtR8Uh8GO1Nv5wpxW7VFVwHcCEr4wyA8/MHiRkO8uHoR5ntAA8Uq3P1vvMTX/BeQiRVSpDGLd+Wn5HNOTA==", - "dev": true - } - } - }, - "eslint-plugin-import": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.13.0.tgz", - "integrity": "sha512-t6hGKQDMIt9N8R7vLepsYXgDfeuhp6ZJSgtrLEDxonpSubyxUZHjhm6LsAaZX8q6GYVxkbT3kTsV9G5mBCFR6A==", - "dev": true, - "requires": { - "contains-path": "^0.1.0", - "debug": "^2.6.8", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.1", - "eslint-module-utils": "^2.2.0", - "has": "^1.0.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.3", - "read-pkg-up": "^2.0.0", - "resolve": "^1.6.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - } - } - }, - "eslint-plugin-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-7.0.1.tgz", - "integrity": "sha512-lfVw3TEqThwq0j2Ba/Ckn2ABdwmL5dkOgAux1rvOk6CO7A6yGyPI2+zIxN6FyNkp1X1X/BSvKOceD6mBWSj4Yw==", - "dev": true, - "requires": { - "eslint-plugin-es": "^1.3.1", - "eslint-utils": "^1.3.1", - "ignore": "^4.0.2", - "minimatch": "^3.0.4", - "resolve": "^1.8.1", - "semver": "^5.5.0" - } - }, - "eslint-plugin-promise": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz", - "integrity": "sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ==", - "dev": true - }, - "eslint-plugin-standard": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz", - "integrity": "sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w==", - "dev": true - }, - "eslint-scope": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", - "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", - "dev": true, - "requires": { - "acorn": "^5.6.0", - "acorn-jsx": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -1611,9 +1182,9 @@ } }, "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -1636,17 +1207,6 @@ } } }, - "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", - "dev": true, - "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", - "tmp": "^0.0.33" - } - }, "extglob": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", @@ -1719,45 +1279,25 @@ } }, "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.0.tgz", + "integrity": "sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=" }, "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" - } - }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -1795,34 +1335,12 @@ "unpipe": "~1.0.0" } }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", - "dev": true, - "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" - } - }, "follow-redirects": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.0.tgz", - "integrity": "sha512-fdrt472/9qQ6Kgjvb935ig6vJCuofpBUD14f9Vb+SLlm7xIe4Qva5gey8EKtv8lp7ahE1wilg3xL1znpVGtZIA==", + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "requires": { - "debug": "^3.1.0" + "debug": "=3.1.0" }, "dependencies": { "debug": { @@ -1852,12 +1370,12 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "requires": { "asynckit": "^0.4.0", - "combined-stream": "1.0.6", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, @@ -1886,12 +1404,6 @@ "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=", "dev": true }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, "fsevents": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", @@ -1912,7 +1424,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -1933,12 +1446,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1953,17 +1468,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2080,7 +1598,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2092,6 +1611,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2106,6 +1626,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2113,12 +1634,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -2137,6 +1660,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -2217,7 +1741,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2229,6 +1754,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -2314,7 +1840,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -2350,6 +1877,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -2369,6 +1897,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -2412,12 +1941,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -2426,12 +1957,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "get-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", @@ -2494,48 +2019,6 @@ "ini": "^1.3.4" } }, - "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, "got": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", @@ -2577,40 +2060,14 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { - "ajv": "^5.1.0", + "ajv": "^6.5.5", "har-schema": "^2.0.0" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - } - } - }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2663,9 +2120,9 @@ } }, "hash.js": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", - "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -2681,11 +2138,30 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } }, "http-errors": { "version": "1.6.3", @@ -2718,12 +2194,6 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, - "ignore": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.2.tgz", - "integrity": "sha512-uoxnT7PYpyEnsja+yX+7v49B7LXxmzDJ2JALqHH3oEGzpM2U1IGcbfnOr8Dt57z3B/UWs7/iAgPFbmye8m4I0g==", - "dev": true - }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -2762,27 +2232,6 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, - "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } - }, "ipaddr.js": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", @@ -2797,12 +2246,6 @@ "kind-of": "^3.0.2" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", @@ -2817,21 +2260,6 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", - "dev": true - }, "is-ci": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", @@ -2850,12 +2278,6 @@ "kind-of": "^3.0.2" } }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", - "dev": true - }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -2941,21 +2363,6 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "requires": { - "is-path-inside": "^1.0.0" - } - }, "is-path-inside": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", @@ -2974,33 +2381,12 @@ "isobject": "^3.0.1" } }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, "is-redirect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", "dev": true }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "dev": true, - "requires": { - "has": "^1.0.1" - } - }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, "is-retry-allowed": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", @@ -3012,12 +2398,6 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, - "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -3050,27 +2430,15 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } + "jquery": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==" }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "json-schema": { "version": "0.2.3", @@ -3078,15 +2446,9 @@ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stringify-safe": { "version": "5.0.1", @@ -3102,6 +2464,13 @@ "extsprintf": "1.3.0", "json-schema": "0.2.3", "verror": "1.10.0" + }, + "dependencies": { + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + } } }, "kind-of": { @@ -3127,58 +2496,10 @@ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", "optional": true }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lodash.debounce": { "version": "4.0.8", @@ -3199,7 +2520,8 @@ "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "optional": true }, "lowercase-keys": { "version": "1.0.1", @@ -3328,12 +2650,6 @@ "mime-db": "~1.33.0" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -3378,23 +2694,6 @@ } } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, "moment": { "version": "2.22.2", "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", @@ -3430,16 +2729,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, "nan": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==" + "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "dev": true, + "optional": true }, "nanomatch": { "version": "1.2.13", @@ -3474,23 +2769,11 @@ } } }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, - "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", - "dev": true - }, "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", @@ -3561,18 +2844,6 @@ "abbrev": "1" } }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, "normalize-path": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", @@ -3591,16 +2862,18 @@ "path-key": "^2.0.0" } }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-copy": { "version": "0.1.0", @@ -3671,54 +2944,17 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - }, - "dependencies": { - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - } + "wrappy": "1" } }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + } }, "p-finally": { "version": "1.0.0", @@ -3731,30 +2967,6 @@ "resolved": "https://registry.npmjs.org/p-iteration/-/p-iteration-1.1.7.tgz", "integrity": "sha512-VsYvUPjm2edbKkX4QzlASC1qB2e4Z6IE9WPaRVHKwCtobmB6vfUcU9eBOwj1k5uMNi8O6w89QfsDatO5ePSjQg==" }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, "package-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", @@ -3767,13 +2979,12 @@ "semver": "^5.1.0" } }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", "requires": { - "error-ex": "^1.2.0" + "@types/node": "*" } }, "parseurl": { @@ -3793,15 +3004,6 @@ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", "dev": true }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3819,34 +3021,11 @@ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", "dev": true }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -3867,36 +3046,6 @@ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", "dev": true }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "^1.0.0" - } - }, - "pluralize": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -3908,12 +3057,6 @@ "resolved": "https://registry.npmjs.org/postinstall-build/-/postinstall-build-5.0.1.tgz", "integrity": "sha1-uRepB5smF42aJK9aXNjLSpkdEbk=" }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", @@ -3926,12 +3069,6 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, - "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", - "dev": true - }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -3963,6 +3100,11 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + }, "pstree.remy": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.0.tgz", @@ -3973,9 +3115,9 @@ } }, "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { "version": "6.5.1", @@ -4041,38 +3183,6 @@ } } }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - } - } - }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -4110,21 +3220,6 @@ "safe-regex": "^1.1.0" } }, - "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", - "dev": true, - "requires": { - "define-properties": "^1.1.2" - } - }, - "regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", - "dev": true - }, "registry-auth-token": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", @@ -4162,47 +3257,54 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", + "aws4": "^1.8.0", "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" - } - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "uuid": "^3.3.2" }, "dependencies": { - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true + "mime-db": { + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" + }, + "mime-types": { + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "requires": { + "mime-db": "1.42.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" } } }, @@ -4215,15 +3317,6 @@ "semver": "^5.1.0" } }, - "resolve": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", - "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", - "dev": true, - "requires": { - "path-parse": "^1.0.5" - } - }, "resolve-from": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", @@ -4235,16 +3328,6 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -4260,31 +3343,6 @@ "align-text": "^0.1.1" } }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "^7.0.5" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, "ripemd160": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", @@ -4294,24 +3352,6 @@ "inherits": "^2.0.1" } }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", - "dev": true, - "requires": { - "symbol-observable": "1.0.1" - } - }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", @@ -4332,18 +3372,42 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "secp256k1": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.0.tgz", - "integrity": "sha512-e5QIJl8W7Y4tT6LHffVcZAxJjvpgE5Owawv6/XCYPQljE9aP2NFFddQ8OYMKhdLshNu88FfL3qCN3/xYkXGRsA==", - "requires": { - "bindings": "^1.2.1", - "bip66": "^1.1.3", - "bn.js": "^4.11.3", - "create-hash": "^1.1.2", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.1.tgz", + "integrity": "sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g==", + "requires": { + "bindings": "^1.5.0", + "bip66": "^1.1.5", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", "drbg.js": "^1.0.1", - "elliptic": "^6.2.3", - "nan": "^2.2.1", - "safe-buffer": "^5.1.0" + "elliptic": "^6.4.1", + "nan": "^2.14.0", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + } } }, "secure-random": { @@ -4458,15 +3522,6 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0" - } - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -4607,38 +3662,6 @@ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", "dev": true }, - "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", - "dev": true - }, "split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", @@ -4657,16 +3680,34 @@ "extend-shallow": "^3.0.0" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "sscjs": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/sscjs/-/sscjs-0.0.8.tgz", + "integrity": "sha512-uu7zaFHUqUL5YySNrRg0MKN9PtZA356mX080ETJgGXNy7Wdc1RgwxHWbZJySTyywZRjn3BdIZRrNCEId6AS+Yw==", + "requires": { + "axios": "^0.19.0" + }, + "dependencies": { + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + } + } }, "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -4748,24 +3789,10 @@ "strip-ansi": "^4.0.0" } }, - "string.prototype.matchall": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-2.0.0.tgz", - "integrity": "sha512-WoZ+B2ypng1dp4iFLF2kmZlwwlE19gmjgKuhL1FJfDgCREWb3ye3SDVHSzLH6bxfnvYmkCxbzkmWcQZHA4P//Q==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.10.0", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "regexp.prototype.flags": "^1.2.0" - } - }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -4779,12 +3806,6 @@ "ansi-regex": "^3.0.0" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -4806,52 +3827,6 @@ "has-flag": "^3.0.0" } }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - }, - "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", - "dev": true, - "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, "term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -4861,12 +3836,6 @@ "execa": "^0.7.0" } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -4879,15 +3848,6 @@ "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", "dev": true }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, "to-object-path": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", @@ -4929,11 +3889,19 @@ } }, "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { + "psl": "^1.1.24", "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } } }, "tunnel-agent": { @@ -4947,17 +3915,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-is": { "version": "1.6.16", @@ -5130,17 +4088,8 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, "requires": { "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } } }, "urix": { @@ -5178,8 +4127,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", @@ -5187,19 +4135,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", - "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" - }, - "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" }, "vary": { "version": "1.1.2", @@ -5254,15 +4192,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, "write-file-atomic": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", From adfb27f9ae53e6cd62ffb039918670010600182d Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 Nov 2019 17:58:02 +0200 Subject: [PATCH 127/193] Implement recentAuthorsData & totalPostsSubmitted - End point for returning recentAuthorsData - End point for returning totalPostsSubmitted --- app.js | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/app.js b/app.js index 38e0d6a..04c446f 100644 --- a/app.js +++ b/app.js @@ -3020,7 +3020,95 @@ rewardActifitTokenWeb = async function (req, reward_activity) { } +/* end point for returning total post count on a specific date */ +app.get('/recentVerifiedPosts', async function(req, res) { + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + var endDate = moment(moment(startDate).utc().add(2, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + + let maxCount = 100; + if (req.query.maxCount && !isNaN(req.query.maxCount)){ + maxCount = parseInt(req.query.maxCount); + } + + await db.collection('verified_posts').aggregate([ + {$match: + { + date: { + $lte: new Date(endDate), + $gt: new Date(startDate) + }, + author: { + $ne: '', + } + }, + }, + {$sort: + { + date:1 + }, + }, + {$group: + { + _id: '$author', + } + } + ]).limit(maxCount).toArray(function(err, results) { + //also append total token count to the grouped display + console.log(results.length); + res.send(results); + }); + +}); + +/* end point for returning total post count on a specific date */ +app.get('/recentAuthorsData', async function(req, res) { + + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + var endDate = moment(moment(startDate).utc().add(1, 'days').toDate()).format('YYYY-MM-DD'); + let maxCount = 10; + if (req.query.maxCount && !isNaN(req.query.maxCount)){ + maxCount = parseInt(req.query.maxCount); + } + console.log("startDate:"+startDate+" endDate:"+endDate); + + await db.collection('verified_posts').aggregate([ + { + $match: + { + "date": { + "$lte": new Date(endDate), + "$gt": new Date(startDate) + } + } + } + ]).toArray(async function(err, results) { + //also append total token count to the grouped display + console.log(results.length); + results = results.reverse(); + let finalSet = []; + if (!req.params){ + req.params = new Object(); + } + for (let i=0;i < maxCount;i++){ + req.params.user = results[i].author; + let rank = await calcRank (req, res); + console.log(results[i].author); + console.log(rank); + finalSet.push({'author': results[i].author, 'rank': rank}); + } + res.send(finalSet); + + }); + +}); /* end point for returning total post count on a specific date */ app.get('/totalPostsSubmitted', async function(req, res) { From 701b27237a5f75a3b2b565e5d7e01cb60b91f35d Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 Nov 2019 18:00:23 +0200 Subject: [PATCH 128/193] Implement Friendship & Notifications Implement Friendship & Notifications functionality and end points --- app.js | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 241 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index 04c446f..e6ec55e 100644 --- a/app.js +++ b/app.js @@ -36,6 +36,16 @@ MongoClient.connect(url, function(err, client) { db = client.db(db_name); + //print version + /* var adminDb = db.admin(); + adminDb.serverStatus(function(err, info) { + if (err){ + console.log(err); + }else{ + console.log(info.version); + } + })*/ + // Get the documents collection collection = db.collection(collection_name); } else { @@ -90,6 +100,8 @@ app.get('/', function (req, res) { //initial load let account = null; +let accountRefresh = false; +let accountQueries = 0; loadAccountData(); async function loadAccountData(){ @@ -579,6 +591,219 @@ app.get('/userBadges/:user', async function (req, res) { res.send(user); }); + +/* end point for fetching user's friends */ +app.get('/userFriends/:user', async function (req, res) { + let friendsA = await db.collection('friends').find({userA: req.params.user}, {fields : {userB:1, _id:0}}).toArray(); + let friendsB = await db.collection('friends').find({userB: req.params.user}, {fields : {userA:1, _id:0}}).toArray(); + console.log(friendsA); + console.log(friendsB); + friendsA = JSON.parse(JSON.stringify(friendsA).replace(/userB/g,'friend')); + friendsB = JSON.parse(JSON.stringify(friendsB).replace(/userA/g,'friend')); + res.send(friendsA.concat(friendsB)); +}); + +sendNotification = async function (user, action_taker, type, details, url){ + let notification_entry = { + user: user, + action_taker: action_taker, + type: type, + details: details, + url: url, + date: new Date(), + status: 'unread', + }; + try{ + let transaction = await db.collection('notifications').insert(notification_entry); + console.log('success inserting notification data'); + return true; + }catch(err){ + console.log('error'); + return false; + } +} + +/* end point for marking a notification as read */ +app.get('/markRead/:notif_id', async function (req, res) { + let notif_to_update = { + _id: new ObjectId(req.params.notif_id), + }; + try{ + let transaction = await db.collection('notifications').update(notif_to_update, { $set: {status: 'read'} } ); + console.log('success updating notification status'); + res.send({status: 'success'}); + }catch(err){ + console.log('error'); + res.send({status: 'error'}); + } + + +}); +/* end point for fetching pending user's friend requests */ +app.get('/userFriendRequests/:user', async function (req, res) { + let user_requests = await db.collection('user_requests').find({initiator: req.params.user, status:'pending'}).toArray(); + let user_targets = await db.collection('user_requests').find({target: req.params.user, status:'pending'}).toArray(); + res.send({sent_pending: user_requests, received_pending: user_targets}); +}); + +/* end point for adding user's friend */ +app.get('/addFriend/:userA/:userB/:blockNo/:trxID', async function (req, res) { + //ensure proper transaction + let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'add-friend-request', req.params.blockNo, req.params.trxID); + if (!ver_trx){ + res.send({status: 'error'}); + return; + } + + let user_friendship = { + initiator: req.params.userA, + request: 'friendship', + target: req.params.userB, + date: new Date(), + status: 'pending', + }; + try{ + let transaction = await db.collection('user_requests').insert(user_friendship); + console.log('success inserting post data'); + + //notify recipient + sendNotification(req.params.userB, req.params.userA, 'friendship_request', 'User ' + req.params.userA + ' has sent you a friendship request', 'https://actifit.io/'+req.params.userA); + + res.send({status: 'success'}); + }catch(err){ + console.log('error'); + res.send({status: 'error'}); + } + +}); + + +/* end point for cancelling friend request */ +app.get('/cancelFriendRequest/:userA/:userB/:blockNo/:trxID', async function (req, res) { + //ensure proper transaction + let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'cancel-friend-request', req.params.blockNo, req.params.trxID); + if (!ver_trx){ + res.send({status: 'error'}); + return; + } + let friendshipQuery = { + initiator: req.params.userA, + target: req.params.userB, + request: 'friendship', + status: 'pending', + } + let userFriendship = { + initiator: req.params.userA, + request: 'friendship', + target: req.params.userB, + date: new Date(), + status: 'cancelled', + }; + try{ + let transaction = await db.collection('user_requests').update(friendshipQuery, userFriendship, { upsert: true }); + console.log('success inserting post data'); + res.send({status: 'success'}); + }catch(err){ + console.log('error'); + res.send({status: 'error'}); + } + +}); + + + +/* end point for cancelling friend request */ +app.get('/acceptFriend/:userA/:userB/:blockNo/:trxID', async function (req, res) { + //ensure proper transaction + let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'accept-friendship', req.params.blockNo, req.params.trxID); + if (!ver_trx){ + res.send({status: 'error'}); + return; + } + //need to update both ways to check which way was the original request + let friendshipQuery = { + initiator: req.params.userB, + target: req.params.userA, + request: 'friendship', + } + let userFriendship = { + initiator: req.params.userB, + request: 'friendship', + target: req.params.userA, + date: new Date(), + status: 'approved', + }; + let insertSuccess = false; + try{ + let transaction = await db.collection('user_requests').update(friendshipQuery, userFriendship); + console.log('success updating post data'); + + //notify recipient + sendNotification(req.params.userB, req.params.userA, 'friendship_acceptance', 'User ' + req.params.userA + ' has accepted your friendship request', 'https://actifit.io/'+req.params.userA); + + insertSuccess = true; + }catch(err){ + console.log('error'); + res.send({status: 'error'}); + } + + if (insertSuccess){ + + //also insert to friendship table + + let friendshipEntry = { + userA: req.params.userB, + userB: req.params.userA, + date: new Date(), + }; + + try{ + let result = await db.collection('friends').insert(friendshipEntry); + res.send({status: 'success'}); + }catch(err){ + res.send({status: 'error', details: err}); + } + } +}); + + +/* end point for dropping friendship */ +app.get('/dropFriendship/:userA/:userB/:blockNo/:trxID', async function (req, res) { + //ensure proper transaction + let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'cancel-friendship', req.params.blockNo, req.params.trxID); + if (!ver_trx){ + res.send({status: 'error'}); + return; + } + try{ + //remove friendship entry both ways + let result = await db.collection('friends').remove({userA: req.params.userA, userB: req.params.userB}); + console.log(result); + result = await db.collection('friends').remove({userA: req.params.userB, userB: req.params.userA}); + console.log(result); + + //also remove requests to prevent confusion + result = await db.collection('user_requests').remove({initiator: req.params.userA, request: 'friendship', target: req.params.userB}); + result = await db.collection('user_requests').remove({initiator: req.params.userB, request: 'friendship', target: req.params.userA}); + + res.send({'status': 'success'}); + }catch(err){ + console.log(err); + res.send({status: 'error'}); + } +}); + +/* end point for fetching all users notifications */ +app.get('/activeNotifications/:user', async function (req, res) { + let activeNotifications = await db.collection('notifications').find({user: req.params.user, status: 'unread'}).toArray(); + res.send(activeNotifications.reverse()); +}); + +/* end point for fetching all users notifications */ +app.get('/readNotifications/:user', async function (req, res) { + let activeNotifications = await db.collection('notifications').find({user: req.params.user, status: 'read'}).toArray(); + res.send(activeNotifications.reverse()); +}); /* end point for fetching all users badges */ app.get('/allUserBadges/', async function (req, res) { let badges = await db.collection('user_badges').find().toArray(); @@ -713,6 +938,15 @@ app.get('/delegation/:user', async function (req, res) { res.send(user); }); + +isModerator = async function(userName){ + let entryFound = false + let moderatorList = await db.collection('team').find({name: userName, title:'moderator', status:'active'}).toArray(); + if (Array.isArray(moderatorList) && moderatorList.length>0){ + entryFound = true; + } + return entryFound; +} moderatorsListFunc = async function () { let moderatorList = await db.collection('team').find({title:'moderator', status:'active'}).sort({name: 1}).toArray(); return moderatorList; @@ -1301,11 +1535,7 @@ app.get('/userRewardedPostCount/:user', async function (req, res) { } }); -/* end point for getting current user's Actifit rank */ -app.get('/getRank/:user', async function (req, res) { - - if (typeof req.params.user!= "undefined" && req.params.user!=null){ - +calcRank = async function (req, res){ //delegation calculation matrix var delegation_rules = [ [9,0], @@ -1464,7 +1694,13 @@ app.get('/getRank/:user', async function (req, res) { recent_posts_score:recent_posts_score }); console.log(score_components) + return score_components; +} +/* end point for getting current user's Actifit rank */ +app.get('/getRank/:user', async function (req, res) { + if (typeof req.params.user!= "undefined" && req.params.user!=null){ + let score_components = await calcRank(req, res); res.send(score_components); }else{ res.send(""); From 46ccd29bc77da809a786e7d87fe4588d198de366 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 Nov 2019 18:01:33 +0200 Subject: [PATCH 129/193] Fix several issues Fix several issues including returning empty data if user does not exist, causing problems on subsequent API calls --- app.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index e6ec55e..7d25c0e 100644 --- a/app.js +++ b/app.js @@ -156,13 +156,18 @@ async function getAFITXUserData(user){ /* function handles calculating and returning user token count */ grabUserTokensFunc = async function (username){ - let user = await collection.findOne({_id: username}); + let user = await db.collection('user_tokens').findOne({_id: username}); console.log(user); //fixing token amount display for 3 digits if (typeof user!= "undefined" && user!=null){ if (typeof user.tokens!= "undefined"){ user.tokens = user.tokens.toFixed(3) } + }else{ + user = new Object(); + user._id=username; + user.name=username; + user.tokens=0; } return user; } @@ -187,12 +192,20 @@ generatePassword = function (multip) { app.get('/votingStatus', async function (req, res) { let votingStatus = await db.collection('voting_status').findOne({}); - if (!account){ + accountQueries += 1; + if (accountQueries > 10){ + accountQueries = 0; + accountRefresh = true; + } + //fetch anew account data if account is empty or we need to refresh account data + if (!account || accountRefresh){ + console.log('refreshing account data'); account = await utils.getAccountData(config.account); + accountRefresh = false; } let vp_res = await utils.getVotingPower(account); - let reward_start = utils.toTimer(utils.timeTilKickOffVoting(vp_res * 100)); + let reward_start = utils.toHrMn(utils.timeTilKickOffVoting(vp_res * 100)); res.send({'status': votingStatus, 'vp': vp_res, 'reward_start': reward_start}); }); From 9efef3a4886e339a27a67df515ab5e6fee85e331 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 Nov 2019 18:02:01 +0200 Subject: [PATCH 130/193] Implement Gadget Functionality Implement Gadget Functionality --- app.js | 303 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) diff --git a/app.js b/app.js index 7d25c0e..2ab1383 100644 --- a/app.js +++ b/app.js @@ -652,6 +652,156 @@ app.get('/markRead/:notif_id', async function (req, res) { }); + +/* end point for tracking gadget buy orders */ +app.get('/buyGadget/:user/:gadget/:blockNo/:trxID', async function (req, res) { + + //ensure proper transaction + let ver_trx = await utils.verifyGadgetTransaction(req.params.user, req.params.gadget, 'buy-gadget', req.params.blockNo, req.params.trxID); + if (!ver_trx){ + res.send({status: 'error'}); + return; + } + + //confirmed, register transaction and deduct AFIT tokens + + let user = req.params.user; + let product_id = req.params.gadget; + + //fetch product info + let product = await grabProductInfo (product_id); + if (!product){ + res.send({'error': 'Product not found'}); + return; + } + + //confirm proper AFIT token balance. Test against product price + let user_info = await grabUserTokensFunc (user); + console.log(user_info); + let cur_user_token_count = parseFloat(user_info.tokens); + + let price_options = product.price; + let price_options_count = price_options.length; + let item_price = 0; + let item_currency = 'AFIT'; + let actifit_percent_cut = 10; + for (let i=0; i < price_options_count; i++){ + let entry = price_options[i]; + item_price = entry.price; + item_currency = entry.currency; + actifit_percent_cut = entry.actifit_percent_cut; + } + + if (cur_user_token_count < item_price){ + res.send({'error': 'Account does not have enough AFIT funds'}); + return; + } + + product.provider = 'actifit'; + + //perform transaction + let productBuyTrans = { + user: user, + reward_activity: 'Buy Product', + buyer: user, + seller: product.provider, + product_id: product_id, + product_type: product.type, + product_name: product.name, + product_level: product.level, + product_price: item_price, + token_count: -item_price, + note: 'Bought Product '+product.name+ ' Level '+product.level, + date: new Date(), + } + try{ + console.log(productBuyTrans); + let transaction = await db.collection('token_transactions').insert(productBuyTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing buy action. DB storing issue'}); + return; + } + + //store into user_gadgets table as well + let userGadgetTrans = { + user: user, + gadget: new ObjectId(product_id), + product_type: product.type, + gadget_name: product.name, + gadget_level: product.level, + status: "bought", + span: parseInt(product.benefits.time_span), + span_unit: product.benefits.time_unit, + consumed: 0, + posts_consumed: [], + date_bought: new Date(), + last_updated: new Date(), + note: 'Bought Product '+product.name+ ' Level '+product.level, + } + try{ + console.log(userGadgetTrans); + let transaction = await db.collection('user_gadgets').insert(userGadgetTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing buy action. DB storing issue'}); + return; + } + + //decrease product available count + product.count = parseInt(product.count) - 1; + //extreme case + if (product.count < 0) { + product.count = 0; + } + try{ + let trans = await db.collection('products').save(product); + console.log('success updating user token count'); + }catch(err){ + console.log(err); + } + + //store this in escrow + /*let productSellTrans = { + user: config.null_account,//targetAccount,//product.provider,//config.escrow_account, + reward_activity: 'Sell Product', + buyer: user, + seller: product.provider, + product_id: product_id, + product_type: product.type, + product_price: item_price, + token_count: item_price, + actifit_percent_cut: actifit_percent_cut, + note: 'Sold Product '+product.name+ ' to '+user, + date: new Date(), + } + + try{ + console.log(productSellTrans); + let transaction = await db.collection('token_transactions').insert(productSellTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing sell action. DB storing issue'}); + return; + }*/ + + //update current user's token balance & store to db + let new_token_count = cur_user_token_count - parseFloat(item_price); + user_info.tokens = new_token_count; + console.log('new_token_count:'+new_token_count); + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success updating user token count'); + }catch(err){ + console.log(err); + } + + res.send({'status': 'Success', 'user_tokens': user_info.tokens}); +}); + /* end point for fetching pending user's friend requests */ app.get('/userFriendRequests/:user', async function (req, res) { let user_requests = await db.collection('user_requests').find({initiator: req.params.user, status:'pending'}).toArray(); @@ -2431,6 +2581,159 @@ matchAccessToken = async function (user, product_id, access_token){ return token_match; } +app.get("/gadgetBoughtName", async function(req, res) { + console.log('gadgetBought'); + console.log(req.query); + //check if proper params sent + if (!req.query.user || !req.query.gadget_name || !req.query.gadget_level) { + //make sure all params are sent + res.send({'error':'generic error'}); + } + + let user = req.query.user; + let gadget_name = req.query.gadget_name; + let gadget_level = req.query.gadget_level; + + //check if the proper access token is valid for this user/product combination + let gadget_match = await db.collection('user_gadgets').find( + { user: user, gadget_name: gadget_name, gadget_level: parseInt(gadget_level)}, + + ).toArray(); + + console.log(gadget_match); + + //let token_match = await matchProductTrans(user, gadget_id); + + res.send(gadget_match); +}); + + +app.get("/gadgetsBought", async function(req, res){ + let gadgets = await db.collection('user_gadgets').find().toArray(); + res.send(gadgets); +}); + +app.get("/friendships", async function(req, res){ + let gadgets = await db.collection('friends').find().toArray(); + res.send(gadgets); +}); + +app.get("/userRequests", async function(req, res){ + let gadgets = await db.collection('user_requests').find().toArray(); + res.send(gadgets); +}); + + +app.get("/activeGadgets", async function(req, res) { + //let gadget_match = await db.collection('user_gadgets').find({ status: "active"}).toArray(); + let gadget_match = await db.collection('user_gadgets').aggregate([ + { $match: { status: "active" } }, + { $lookup:{ + from: "products", + /*let: { "user_gadget_id": "$gadget" }, + pipeline: [ + { $addFields: {gadgetId: { $toObjectId: "$$user_gadget_id" }}},//not supported in mongodb < 4 + { $match: { $expr: { $eq: [ "$_id", "$user_gadget_id" ] } } } + ],*/ + localField: "gadget", + foreignField: "_id", + as: "productdetails" + } + }, + + ]).toArray(); + console.log(gadget_match); + res.send(gadget_match); +}); + +app.get("/gadgetBought", async function(req, res) { + console.log('gadgetBought'); + console.log(req.query); + //check if proper params sent + if (!req.query.user || !req.query.gadget_id) { + //make sure all params are sent + res.send({'error':'generic error'}); + } + + let user = req.query.user; + let gadget_id = new ObjectId(req.query.gadget_id); + + //check if the proper access token is valid for this user/product combination + let gadget_match = await db.collection('user_gadgets').find( + { user: user, gadget: gadget_id }, + { user: 1, date_bought: 1 } + ).toArray(); + + console.log(gadget_match); + + //let token_match = await matchProductTrans(user, gadget_id); + + res.send(gadget_match); +}); + + +//end point handles activating a bought gadget +app.get('/activateGadget/:user/:gadget/:blockNo/:trxID/:benefic?', async function (req, res) { + let user = req.params.user; + let gadget = req.params.gadget; + + //make sure friend and user are different + if (req.params.benefic && req.params.benefic.replace('@','') == user){ + res.send({'error': 'User & friend cannot be the same account'}); + return; + } + + console.log('activateGadget'); + let ver_trx = await utils.verifyGadgetTransaction(user, gadget, 'activate-gadget', req.params.blockNo, req.params.trxID); + console.log(ver_trx); + //ensure proper transaction + if (!ver_trx){ + res.send({status: 'error'}); + return; + } + + //find item to activate and proceed activating + gadget = new ObjectId(gadget); + let gadget_match = await db.collection('user_gadgets').findOne({ user: user, gadget: gadget, status: "bought" }); + if (gadget_match){ + gadget_match.status="active"; + if (req.params.benefic){ + gadget_match.benefic = req.params.benefic; + } + db.collection('user_gadgets').save(gadget_match); + res.send({'status': 'success'}); + }else{ + res.send({'error': 'Product not found'}); + + } +}); + +//end point handles deactivating a bought gadget +app.get('/deactivateGadget/:user/:gadget/:blockNo/:trxID', async function (req, res) { + let user = req.params.user; + let gadget = req.params.gadget; + + //ensure proper transaction + let ver_trx = await utils.verifyGadgetTransaction(user, gadget, 'deactivate-gadget', req.params.blockNo, req.params.trxID); + if (!ver_trx){ + res.send({status: 'error'}); + return; + } + + //find item to activate and proceed activating + gadget = new ObjectId(gadget); + let gadget_match = await db.collection('user_gadgets').findOne({ user: user, gadget: gadget, status: "active" }); + if (gadget_match){ + gadget_match.status="bought"; + db.collection('user_gadgets').save(gadget_match); + res.send({'status': 'success'}); + }else{ + res.send({'error': 'Product not found'}); + + } +}); + + app.get("/productBought", async function(req, res) { console.log('productBought'); console.log(req.query); From 293ca019cd919fabdbb553f95fbfcff2d20f4e85 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 Nov 2019 18:09:12 +0200 Subject: [PATCH 131/193] Implement Util based changes Implement Util based changes relating to friends, gadgets and other fixes --- utils.js | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/utils.js b/utils.js index b2ea7c3..0958911 100644 --- a/utils.js +++ b/utils.js @@ -5,7 +5,10 @@ const axios = require('axios'); const dsteem = require('dsteem'); const moment = require('moment') const steem_node = 'https://api.steemit.com';//'https://steemd.minnowsupportproject.org';//'https://api.steem.house';// -const client = new dsteem.Client(steem_node); + +getConfig(); + +const client = new dsteem.Client(config.active_node); var config; @@ -89,7 +92,8 @@ var HOURS = 60 * 60; } const currentManaPerc = currentMana * 100 / maxMana; - + console.log(currentManaPerc); + console.log(account.name); return currentManaPerc; } @@ -576,6 +580,12 @@ function format(n, c, d, t) { return padLeft(h, 2) + ':' + padLeft(m, 2) + ':' + padLeft(s, 2); } + function toHrMn(ts) { + var h = Math.floor(ts / HOURS); + var m = Math.floor((ts % HOURS) / 60); + return padLeft(h, 2) + 'hr(s):' + padLeft(m, 2) + 'min(s)'; + } + function padLeft(v, d) { var l = (v + '').length; if (l >= d) return v + ''; @@ -1037,6 +1047,67 @@ function sortArrLodash (arrToSort) { return _.orderBy(arrToSort, function (o) { return new Number(o.balance)},['desc']); } +async function rewardPost(post_url, vp){ + //extract author and permalink from full url + //check if string ends with /, remove it + if (post_url.slice(-1) == '/'){ + post_url = post_url.slice(0, -1); + } + //last portion is URL + let permalink = post_url.split('/').reverse()[0]; + //before last portion is author, and remove the starting @ + let author = post_url.split('/').reverse()[1].replace('@',''); + //cast vote + let result = await steem.broadcast.voteAsync( + config.rewards_account_pkey, //postingWIF + config.rewards_account, // Voter + author, // Author + permalink, // Permlink + parseFloat(vp)*100, // Weight (10000 = 100%) + ); + return result; +} + +async function verifyGadgetTransaction(userA, gadget_id, tx_type, block_num, tx_id){ + let trx = await client.database.getTransaction({id: tx_id, block_num: block_num}); + try{ + if (trx && trx.operations + && trx.operations.length > 0){ + console.log(trx.operations[0][1]); + let trx_details = trx.operations[0][1]; + let json_data = JSON.parse(trx_details.json); + console.log(trx_details); + if (trx_details.required_posting_auths.length > 0 && trx_details.required_posting_auths[0] == userA + && json_data.transaction == tx_type && json_data.gadget == gadget_id){ + return true; + } + } + }catch(err){ + console.log(err); + } + return false; +} + +async function verifyFriendTransaction(userA, userB, tx_type, block_num, tx_id){ + let trx = await client.database.getTransaction({id: tx_id, block_num: block_num}); + try{ + if (trx && trx.operations + && trx.operations.length > 0){ + console.log(trx.operations[0][1]); + let trx_details = trx.operations[0][1]; + let json_data = JSON.parse(trx_details.json); + console.log(trx_details); + if (trx_details.required_posting_auths.length > 0 && trx_details.required_posting_auths[0] == userA + && json_data.transaction == tx_type && json_data.target == userB){ + return true; + } + + } + }catch(err){ + console.log(err); + } + return false; +} module.exports = { updateSteemVariables: updateSteemVariables, @@ -1052,6 +1123,7 @@ function sortArrLodash (arrToSort) { getCurrency: getCurrency, format: format, toTimer: toTimer, + toHrMn: toHrMn, log: log, calcScore: calcScore, calculateVotes: calculateVotes, @@ -1071,4 +1143,7 @@ function sortArrLodash (arrToSort) { confirmPaymentReceivedBuy: confirmPaymentReceivedBuy, sortArrLodash: sortArrLodash, getAccountData: getAccountData, + rewardPost: rewardPost, + verifyFriendTransaction: verifyFriendTransaction, + verifyGadgetTransaction: verifyGadgetTransaction, } From 020713738424125569f63fde8ac44002024e2a08 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 Nov 2019 18:13:04 +0200 Subject: [PATCH 132/193] Implement AFIT Move transaction rollback Implement AFIT Move transaction rollback --- delegations.js | 132 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 19 deletions(-) diff --git a/delegations.js b/delegations.js index 0f8c672..ec8b237 100644 --- a/delegations.js +++ b/delegations.js @@ -70,6 +70,108 @@ const ssc = new SSC(config.steem_engine_rpc); //moveAFITToSE(true); +//testMove(); + +async function testMove(){ + + + //perform transaction, decrease sender amount + let moveTrans = { + user: 'mcfarhat', + reward_activity: 'test transaction', + token_count: -100, + note: 'User Automated transfer of 100 AFIT to S-E', + date: new Date(), + } + + console.log(moveTrans); + //update our DB + let mongo_conn = config.mongo_uri + if (config.testing){ + mongo_conn = config.mongo_local + } + // Use connect method to connect to the server + MongoClient.connect(mongo_conn, async function (err, dbClient) { + if (!err) { + console.log('Connected successfully to server: ') + + db = dbClient.db(dbName) + let transaction = await db.collection('token_transactions').insert(moveTrans); + + console.log('success inserting move AFIT data'); + + + let json_data = { + contractName: 'tokens', + contractAction: 'transfer', + contractPayload: { + symbol: 'AFIT', + to: 'mcfarhat', + quantity: '100',//needs to be string + memo: '' + } + } + + //broadcast to BC + console.log('broadcast to BC'); + + //sign key properly to function with dsteem requirement + let privateKey = dsteem.PrivateKey.fromString( + //config.token_dist_pkey + config.active_key + ); + let entry = new Object(); + + entry.user='mcfarhat'; + client.broadcast.json({ + required_auths: [config.account], + required_posting_auths: [], + id: 'ssc-mainnet1', + json: JSON.stringify(json_data), + }, privateKey).then( + result => { + console.log('success'); + console.log(result); + updateUserCount(entry); + }, + error => { + console.log('error') + console.error(error) + //roll back transaction + rollBackTrans(moveTrans); + } + ) + + } + }) + +} + +async function rollBackTrans(moveTrans){ + console.log('roll back') + try{ + let transaction = await db.collection('token_transactions').remove(moveTrans); + }catch(err){ + console.log(err); + } +} + +async function updateUserCount(entry){ + //update user total token count + console.log('>>> update user token count'); + let user_info = await db.collection('user_tokens').findOne({_id: entry.user}); + let cur_sender_token_count = parseFloat(user_info.tokens); + let new_token_count = cur_sender_token_count - parseFloat(entry.daily_afit_transfer); + user_info.tokens = new_token_count; + console.log('user:' + entry.user + 'new_token_count:'+new_token_count); + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success updating user token count'); + }catch(err){ + console.log(err); + } +} + async function moveAFITToSE(testMode){ console.log('*** process moving AFIT to SE ***'); @@ -188,22 +290,6 @@ async function moveAFITToSE(testMode){ } console.log('success inserting move AFIT data'); - //update user total token count - console.log('>>> update user token count'); - let user_info = await db.collection('user_tokens').findOne({_id: entry.user}); - let cur_sender_token_count = parseFloat(user_info.tokens); - let new_token_count = cur_sender_token_count - amount; - user_info.tokens = new_token_count; - console.log('user:' + entry.user + 'new_token_count:'+new_token_count); - if (!testMode){ - try{ - let trans = await db.collection('user_tokens').save(user_info); - console.log('success updating user token count'); - }catch(err){ - console.log(err); - } - } - let json_data = { contractName: 'tokens', contractAction: 'transfer', @@ -224,8 +310,16 @@ async function moveAFITToSE(testMode){ id: 'ssc-mainnet1', json: JSON.stringify(json_data), }, privateKey).then( - result => { console.log(result) }, - error => { console.error(error) } + result => { + console.log(result) + //update user total count + updateUserCount(entry); + }, + error => { + console.error(error) + //roll back db transaction as there was issue sending to blockchain + rollBackTrans(moveTrans); + } ) } @@ -1116,4 +1210,4 @@ async function claimRewards(){ }else{ console.log('no rewards to claim for now'); } -} \ No newline at end of file +} From 6c5605d90122feee9c1c5a861cd749024180b87a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2019 17:18:04 +0000 Subject: [PATCH 133/193] Bump mixin-deep from 1.3.1 to 1.3.2 Bumps [mixin-deep](https://github.com/jonschlinkert/mixin-deep) from 1.3.1 to 1.3.2. - [Release notes](https://github.com/jonschlinkert/mixin-deep/releases) - [Commits](https://github.com/jonschlinkert/mixin-deep/compare/1.3.1...1.3.2) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b45fc0..77dd15a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2674,9 +2674,9 @@ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { "for-in": "^1.0.2", From de1f12ad3552302c94c66238468f209064123097 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2019 17:18:04 +0000 Subject: [PATCH 134/193] Bump handlebars from 4.0.11 to 4.5.1 Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.0.11 to 4.5.1. - [Release notes](https://github.com/wycats/handlebars.js/releases) - [Changelog](https://github.com/wycats/handlebars.js/blob/master/release-notes.md) - [Commits](https://github.com/wycats/handlebars.js/compare/v4.0.11...v4.5.1) Signed-off-by: dependabot[bot] --- package-lock.json | 181 ++++++++++------------------------------------ 1 file changed, 39 insertions(+), 142 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3b45fc0..0e87b15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,22 +35,6 @@ "uri-js": "^4.2.2" } }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "optional": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" - }, "ansi-align": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", @@ -138,11 +122,6 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - }, "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", @@ -473,12 +452,6 @@ "unset-value": "^1.0.0" } }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "optional": true - }, "capture-stack-trace": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", @@ -490,16 +463,6 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "optional": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, "chalk": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", @@ -589,25 +552,6 @@ "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", "dev": true }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "optional": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "optional": true - } - } - }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -641,6 +585,12 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "optional": true + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -810,12 +760,6 @@ "ms": "2.0.0" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "optional": true - }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -2044,14 +1988,31 @@ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, "handlebars": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", - "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", + "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", "requires": { - "async": "^1.4.0", + "neo-async": "^2.6.0", "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "uglify-js": { + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.9.tgz", + "integrity": "sha512-pcnnhaoG6RtrvHJ1dFncAe8Od6Nuy30oaJ82ts6//sGSXOP5UjBMEthiProjXmMNHOfd93sqlkztifFMcb+4yw==", + "optional": true, + "requires": { + "commander": "~2.20.3", + "source-map": "~0.6.1" + } + } } }, "har-schema": { @@ -2258,7 +2219,8 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, "is-ci": { "version": "1.1.0", @@ -2477,6 +2439,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -2490,12 +2453,6 @@ "package-json": "^4.0.0" } }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "optional": true - }, "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", @@ -2517,12 +2474,6 @@ "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", "integrity": "sha1-lyHXiLR+C8taJMLivuGg2lXatRQ=" }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "optional": true - }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -2774,6 +2725,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" + }, "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", @@ -3254,7 +3210,8 @@ "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true }, "request": { "version": "2.88.0", @@ -3334,15 +3291,6 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "optional": true, - "requires": { - "align-text": "^0.1.1" - } - }, "ripemd160": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", @@ -3635,14 +3583,6 @@ "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.2.0.tgz", "integrity": "sha512-sWpjPhIZJtqO77GN+LD8dDsDKcWZ9GCOJNqKzi1tvtjGIzwfoyuRH8S0psunmc6Z5P+qfDqztSbwYR5X/e1UTg==" }, - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "requires": { - "amdefine": ">=0.0.4" - } - }, "source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", @@ -3926,31 +3866,6 @@ "mime-types": "~2.1.18" } }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "optional": true, - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "optional": true - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "optional": true - }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", @@ -4176,12 +4091,6 @@ "string-width": "^2.1.1" } }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "optional": true - }, "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", @@ -4223,18 +4132,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "optional": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } } } } From 7a39e7d482575baa7c44206ac0021dd84afd93c2 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 15 Nov 2019 17:01:28 +0200 Subject: [PATCH 135/193] Implement new endpoints Implement new endpoint for fetching user's active gadgets Implement new endpoint for fetching total burnt tokens count and relevant trx count --- app.js | 350 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 194 insertions(+), 156 deletions(-) diff --git a/app.js b/app.js index 2ab1383..77f33cd 100644 --- a/app.js +++ b/app.js @@ -35,7 +35,7 @@ MongoClient.connect(url, function(err, client) { console.log("Connected successfully to server"); db = client.db(db_name); - + //print version /* var adminDb = db.admin(); adminDb.serverStatus(function(err, info) { @@ -313,6 +313,32 @@ app.get('/user-tokens-info', async function(req, res) { }); +app.get('/tokensBurnt', async function (req, res) { + let agg = await db.collection('token_transactions').aggregate([ + { + $match: { + reward_activity: 'Buy Product', + seller: 'actifit' + } + }, + { + $group: + { + _id: null, + tokens_burnt: { $sum: "$token_count" }, + burn_trx_count: { $sum: 1 } + + } + } + ]).toArray(function(err, results) { + if (results.length>0){ + results[0].tokens_burnt = Math.abs(results[0].tokens_burnt); + res.send(results); + console.log(results); + } + }); +}); + /* end point for user total token count display */ app.get('/modAction', async function (req, res) { if (!req.query.moderator || !req.query.fundsPass || !req.query.targetAction){ @@ -604,7 +630,6 @@ app.get('/userBadges/:user', async function (req, res) { res.send(user); }); - /* end point for fetching user's friends */ app.get('/userFriends/:user', async function (req, res) { let friendsA = await db.collection('friends').find({userA: req.params.user}, {fields : {userB:1, _id:0}}).toArray(); @@ -967,6 +992,7 @@ app.get('/readNotifications/:user', async function (req, res) { let activeNotifications = await db.collection('notifications').find({user: req.params.user, status: 'read'}).toArray(); res.send(activeNotifications.reverse()); }); + /* end point for fetching all users badges */ app.get('/allUserBadges/', async function (req, res) { let badges = await db.collection('user_badges').find().toArray(); @@ -1699,167 +1725,167 @@ app.get('/userRewardedPostCount/:user', async function (req, res) { }); calcRank = async function (req, res){ - //delegation calculation matrix - var delegation_rules = [ - [9,0], - [499,0.05], - [999,0.10], - [4999,0.20], - [9999,0.30], - [19999,0.40], - [49999,0.55], - [99999,0.65], - [499999,0.75], - [999999,0.90], - [1000000,1] - ] - - //AFIT token calculation matrix - var afit_token_rules = [ - [9,0], - [999,0.10], - [4999,0.20], - [9999,0.30], - [19999,0.40], - [49999,0.50], - [99999,0.60], - [499999,0.70], - [999999,0.80], - [4999999,0.90], - [5000000,1] - ] - - //Rewarded Posts calculation matrix - var rewarded_posts_rules = [ - [9,0], - [29,0.10], - [59,0.20], - [89,0.30], - [119,0.40], - [179,0.50], - [359,0.60], - [539,0.70], - [719,0.80], - [1079,0.90], - [1080,1] - ] - - //Rewarded Posts calculation matrix - var recent_reward_posts_rules = [ - [0,0], - [2,0.20], - [4,0.40], - [6,0.60], - [8,0.80], - [9,1] - ] - - var user_rank = 0; - - //grab delegation amount - var userDelegations = await activeDelegationFunc(req.params.user); - - let delegSP = 0; - //get current delegated SP if any - if (userDelegations != null){ - console.log('already delegated'); - delegSP = userDelegations.steem_power; - } - //console.log(userDelegations.steem_power); - - var delegation_score = 0; - - //check if the user has an alt account as beneficiary - let delegator_info = await getAltAccountStatusFunc(req.params.user); + //delegation calculation matrix + var delegation_rules = [ + [9,0], + [499,0.05], + [999,0.10], + [4999,0.20], + [9999,0.30], + [19999,0.40], + [49999,0.55], + [99999,0.65], + [499999,0.75], + [999999,0.90], + [1000000,1] + ] + + //AFIT token calculation matrix + var afit_token_rules = [ + [9,0], + [999,0.10], + [4999,0.20], + [9999,0.30], + [19999,0.40], + [49999,0.50], + [99999,0.60], + [499999,0.70], + [999999,0.80], + [4999999,0.90], + [5000000,1] + ] + + //Rewarded Posts calculation matrix + var rewarded_posts_rules = [ + [9,0], + [29,0.10], + [59,0.20], + [89,0.30], + [119,0.40], + [179,0.50], + [359,0.60], + [539,0.70], + [719,0.80], + [1079,0.90], + [1080,1] + ] + + //Rewarded Posts calculation matrix + var recent_reward_posts_rules = [ + [0,0], + [2,0.20], + [4,0.40], + [6,0.60], + [8,0.80], + [9,1] + ] + + var user_rank = 0; + + //grab delegation amount + var userDelegations = await activeDelegationFunc(req.params.user); + + let delegSP = 0; + //get current delegated SP if any + if (userDelegations != null){ + console.log('already delegated'); + delegSP = userDelegations.steem_power; + } + //console.log(userDelegations.steem_power); + + var delegation_score = 0; + + //check if the user has an alt account as beneficiary + let delegator_info = await getAltAccountStatusFunc(req.params.user); + //check if returned object is not empty + if (Object.keys(delegator_info).length > 0){ + if (parseInt(delegator_info.user_rank_benefit) == 1){ + //consider as no delegations + delegSP = 0; + } + }else{ + //also check the other case where the account is an alt-account + delegator_info = await getAltAccountByNameFunc(req.params.user); //check if returned object is not empty - if (Object.keys(delegator_info).length > 0){ - if (parseInt(delegator_info.user_rank_benefit) == 1){ - //consider as no delegations - delegSP = 0; - } - }else{ - //also check the other case where the account is an alt-account - delegator_info = await getAltAccountByNameFunc(req.params.user); - //check if returned object is not empty - if (delegator_info.length > 0){ - for (let x=0, max_limit=delegator_info.length;x 0){ + for (let x=0, max_limit=delegator_info.length;x 0){ - delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(delegSP)); - } - - user_rank += delegation_score; - - //grab user token count - var userTokens = await grabUserTokensFunc(req.params.user); - //console.log(userTokens.tokens); - - var afit_tokens_score = 0; - if (userTokens != null){ - afit_tokens_score = utils.calcScore(afit_token_rules, config.afit_token_factor, parseFloat(userTokens.tokens)); - } - - user_rank += afit_tokens_score; - - //grab total rewarded posts count - var tot_rewarded_post_count = await userRewardedPostCountFunc(req, res); - //console.log(tot_rewarded_post_count); - - var tot_posts_score = utils.calcScore(rewarded_posts_rules, config.rewarded_posts_factor, parseInt(tot_rewarded_post_count)); - - user_rank += tot_posts_score; - - //set the check period for config value of days days, and rerun the call to get last rewarded posting activity during this period - req.query.period = config.recent_posts_period; - - //add a 2 day delay to take into consideration late voting rounds - req.query.delay = 2; - - var recent_rewarded_post_count = await userRewardedPostCountFunc(req, res); - //console.log(recent_rewarded_post_count); - - var recent_posts_score = utils.calcScore(recent_reward_posts_rules, config.recent_posts_factor, parseInt(recent_rewarded_post_count)); - - user_rank += recent_posts_score; - - let rank_no_afitx = user_rank; - //also append AFITX based rank. for every 1 AFITX, increase 0.1 rank - let userHasAFITX = usersAFITXBal.find(entry => entry.account === req.params.user); - let user_rank_afitx = 0; - - if (userHasAFITX){ - user_rank_afitx = (parseFloat(userHasAFITX.balance) / 10).toFixed(2); - //max increase by holding AFITX is 100 - if (user_rank_afitx > 100){ - user_rank_afitx = 100; - } - user_rank += parseFloat(user_rank_afitx); + } + + + if (parseFloat(delegSP) > 0){ + delegation_score = utils.calcScore(delegation_rules, config.delegation_factor, parseFloat(delegSP)); + } + + user_rank += delegation_score; + + //grab user token count + var userTokens = await grabUserTokensFunc(req.params.user); + //console.log(userTokens.tokens); + + var afit_tokens_score = 0; + if (userTokens != null){ + afit_tokens_score = utils.calcScore(afit_token_rules, config.afit_token_factor, parseFloat(userTokens.tokens)); + } + + user_rank += afit_tokens_score; + + //grab total rewarded posts count + var tot_rewarded_post_count = await userRewardedPostCountFunc(req, res); + //console.log(tot_rewarded_post_count); + + var tot_posts_score = utils.calcScore(rewarded_posts_rules, config.rewarded_posts_factor, parseInt(tot_rewarded_post_count)); + + user_rank += tot_posts_score; + + //set the check period for config value of days days, and rerun the call to get last rewarded posting activity during this period + req.query.period = config.recent_posts_period; + + //add a 2 day delay to take into consideration late voting rounds + req.query.delay = 2; + + var recent_rewarded_post_count = await userRewardedPostCountFunc(req, res); + //console.log(recent_rewarded_post_count); + + var recent_posts_score = utils.calcScore(recent_reward_posts_rules, config.recent_posts_factor, parseInt(recent_rewarded_post_count)); + + user_rank += recent_posts_score; + + let rank_no_afitx = user_rank; + //also append AFITX based rank. for every 1 AFITX, increase 0.1 rank + let userHasAFITX = usersAFITXBal.find(entry => entry.account === req.params.user); + let user_rank_afitx = 0; + + if (userHasAFITX){ + user_rank_afitx = (parseFloat(userHasAFITX.balance) / 10).toFixed(2); + //max increase by holding AFITX is 100 + if (user_rank_afitx > 100){ + user_rank_afitx = 100; } - - var score_components = JSON.stringify({ - user_rank: user_rank.toFixed(2), - rank_no_afitx: rank_no_afitx, - afitx_rank: parseFloat(user_rank_afitx), - delegation_score: delegation_score, - afit_tokens_score: afit_tokens_score, - tot_posts_score: tot_posts_score, - recent_posts_score:recent_posts_score - }); - console.log(score_components) + user_rank += parseFloat(user_rank_afitx); + } + + var score_components = JSON.stringify({ + user_rank: user_rank.toFixed(2), + rank_no_afitx: rank_no_afitx, + afitx_rank: parseFloat(user_rank_afitx), + delegation_score: delegation_score, + afit_tokens_score: afit_tokens_score, + tot_posts_score: tot_posts_score, + recent_posts_score:recent_posts_score + }); + console.log(score_components) return score_components; } - + /* end point for getting current user's Actifit rank */ app.get('/getRank/:user', async function (req, res) { if (typeof req.params.user!= "undefined" && req.params.user!=null){ @@ -2646,6 +2672,18 @@ app.get("/activeGadgets", async function(req, res) { res.send(gadget_match); }); + +app.get("/activeGadgetsByUser/:user", async function(req, res) { + //let gadget_match = await db.collection('user_gadgets').find({ status: "active"}).toArray(); + let targetUser = req.params.user.replace('@',''); + let aTargetUser = '@'+targetUser; + let gadget_match = await db.collection('user_gadgets').find({ user: { $in: [targetUser, aTargetUser]}, status: "active" }).toArray(); + let gadget_match_benefic = await db.collection('user_gadgets').find({ benefic: { $in: [targetUser, aTargetUser]}, status: "active" }).toArray(); + res.send({'own': gadget_match, 'benefic': gadget_match_benefic}); +}); + + + app.get("/gadgetBought", async function(req, res) { console.log('gadgetBought'); console.log(req.query); @@ -3574,7 +3612,7 @@ rewardActifitTokenWeb = async function (req, reward_activity) { /* end point for returning total post count on a specific date */ app.get('/recentVerifiedPosts', async function(req, res) { - + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); if (req.query.targetDate){ startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); From ff31404db07e5240baa98748a30f8be5b823aa8d Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Jan 2020 22:20:26 +0200 Subject: [PATCH 136/193] Prevent banned users from suggested list Prevent banned users from being fetched and returned under the suggested list/verified posts --- app.js | 183 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 138 insertions(+), 45 deletions(-) diff --git a/app.js b/app.js index 77f33cd..b7300d7 100644 --- a/app.js +++ b/app.js @@ -2105,6 +2105,71 @@ app.get('/moderatorActivity', async function(req, res) { }); + +/* end point for capturing moderator activity stats during last week */ +app.get('/moderatorWeeklyStats', async function(req, res) { + let moderatorsList = await moderatorsListFunc(); + + //console.log(moment().utc().startOf('date').day()); + //return; + //default today + var startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + + //make sure stats cover up to 1 week + let days = moment().utc().startOf('date').day(); + + //need to fetch last week data if properly set + if (req.query.priorWeek){ + startDate = moment(moment(startDate).utc().subtract(days, 'days').toDate()).format('YYYY-MM-DD'); + days = 7; + } + + var endDate = moment(moment(startDate).utc().subtract(days, 'days').toDate()).format('YYYY-MM-DD'); + console.log("startDate:"+startDate+" endDate:"+endDate); + + await db.collection('team').aggregate([ + { + $match: + { + title:'moderator', + status:'active' + } + }, + { + $lookup: + { + from: "token_transactions", + localField: "name", + foreignField: "user", + as: "moderatorActivity" + } + }, + { + $project: + { + '_id':0, + items: + { + $filter: { + input: "$moderatorActivity", + as: "singleEntry", + cond: { $and: [ + { "$lte": ["$$singleEntry.date", new Date(startDate)] }, + { "$gt": ["$$singleEntry.date", new Date(endDate)] }, + { "$in": ["$$singleEntry.reward_activity", ["Moderator Comment", "Post Vote"]] } + ] } + } + } + } + }, + ]).toArray(function(err, results) { + res.send(results); + console.log(results); + }); + +}); + + /* end point to grab current AFIT token price */ app.get('/curAFITPrice', async function(req, res) { let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next(); @@ -2255,54 +2320,56 @@ app.get('/confirmAFITSEBulk', async function(req,res){ trx_entries.forEach( async function(entry){ console.log(entry); let user = entry.from; - //query to see if entry already stored - let tokenExchangeTransQuery = { - user: user, - se_trx_ref: entry.txid - } - //store the transaction to the user's profile - let tokenExchangeTrans = { - user: user, - reward_activity: 'Move AFIT SE to Actifit Wallet', - token_count: parseFloat(entry.quantity), - se_trx_ref: entry.txid, - date: new Date(entry.timestamp) - } - try{ - console.log(tokenExchangeTrans); - //insert the query ensuring we do not write it twice - let transaction = await db.collection('token_transactions').update(tokenExchangeTransQuery, tokenExchangeTrans, { upsert: true }); - let trans_res = transaction.result; - console.log(trans_res); - - if (trans_res.upserted){ - //we have a new entry, increase user token count - - let user_info = await grabUserTokensFunc (user); + if (user != config.steem_engine_actifit_se){ + //query to see if entry already stored + let tokenExchangeTransQuery = { + user: user, + se_trx_ref: entry.txid + } + //store the transaction to the user's profile + let tokenExchangeTrans = { + user: user, + reward_activity: 'Move AFIT SE to Actifit Wallet', + token_count: parseFloat(entry.quantity), + se_trx_ref: entry.txid, + date: new Date(entry.timestamp) + } + try{ + console.log(tokenExchangeTrans); + //insert the query ensuring we do not write it twice + let transaction = await db.collection('token_transactions').update(tokenExchangeTransQuery, tokenExchangeTrans, { upsert: true }); + let trans_res = transaction.result; + console.log(trans_res); - let cur_user_token_count = 0; - if (user_info){ - cur_user_token_count = parseFloat(user_info.tokens); - //update current user's token balance & store to db - afit_amount = parseFloat(entry.quantity); - let new_token_count = cur_user_token_count + parseFloat(afit_amount); - user_info.tokens = new_token_count; - console.log('new_token_count:'+new_token_count); - try{ - let trans = await db.collection('user_tokens').save(user_info); - console.log('success adding AFIT tokens to user balance'); - }catch(err){ - console.log(err); - return; + if (trans_res.upserted){ + //we have a new entry, increase user token count + + let user_info = await grabUserTokensFunc (user); + + let cur_user_token_count = 0; + if (user_info){ + cur_user_token_count = parseFloat(user_info.tokens); + //update current user's token balance & store to db + afit_amount = parseFloat(entry.quantity); + let new_token_count = cur_user_token_count + parseFloat(afit_amount); + user_info.tokens = new_token_count; + console.log('new_token_count:'+new_token_count); + try{ + let trans = await db.collection('user_tokens').save(user_info); + console.log('success adding AFIT tokens to user balance'); + }catch(err){ + console.log(err); + return; + } } } + + }catch(err){ + console.log(err); + res.write(JSON.stringify({'error': 'Error adding AFIT tokens to user balance'})); + res.end(); + return; } - - }catch(err){ - console.log(err); - res.write(JSON.stringify({'error': 'Error adding AFIT tokens to user balance'})); - res.end(); - return; } }); @@ -2682,6 +2749,25 @@ app.get("/activeGadgetsByUser/:user", async function(req, res) { res.send({'own': gadget_match, 'benefic': gadget_match_benefic}); }); +app.get("/boughtGadgetCountByUser/:user", async function(req, res) { + //let gadget_match = await db.collection('user_gadgets').find({ status: "active"}).toArray(); + let targetUser = req.params.user.replace('@',''); + let aTargetUser = '@'+targetUser; + let gadget_match = await db.collection('user_gadgets').aggregate([ + { $match: { user: { $in: [targetUser, aTargetUser]} } }, + { + $group: + { + _id: {gadget: "$gadget", status: "$status"}, + /*tokens_distributed: { $sum: "$tokens" },*/ + /*active_count: { $sum: }*/ + count: { $sum: 1 } + } + } + ]).toArray(); + console.log(gadget_match); + res.send(gadget_match); +}); app.get("/gadgetBought", async function(req, res) { @@ -3625,6 +3711,13 @@ app.get('/recentVerifiedPosts', async function(req, res) { maxCount = parseInt(req.query.maxCount); } + //fetch banned accounts + let banned_users = await db.collection('banned_accounts').find({ban_status:"active"}, {fields : { user: 1, _id: 0 } }).toArray(); + //console.log(banned_users); + let banned_arr = banned_users.map(entr => entr.user); + banned_arr.push(''); + //console.log(banned_arr); + await db.collection('verified_posts').aggregate([ {$match: { @@ -3633,7 +3726,7 @@ app.get('/recentVerifiedPosts', async function(req, res) { $gt: new Date(startDate) }, author: { - $ne: '', + $nin: banned_arr, } }, }, From 98ae6b314ba3215d6dbdf5d8fab57671ab44064c Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Jan 2020 22:50:35 +0200 Subject: [PATCH 137/193] Prevent banned accounts from top lists Prevent banned accounts from displaying on top holders lists for AFIT & AFITX --- app.js | 22 ++++++++++++++++++++-- utils.js | 12 ++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index b7300d7..7747b8c 100644 --- a/app.js +++ b/app.js @@ -504,9 +504,20 @@ app.get('/topAFITXHolders', async function (req, res) { //set max as 100 maxAmount = 100; } + + //fetch banned accounts + let banned_users = await db.collection('banned_accounts').find({ban_status:"active"}, {fields : { user: 1, _id: 0 } }).toArray(); + //console.log(banned_users); + let banned_arr = banned_users.map(entr => entr.user); + banned_arr.push(''); + + afitxSorted = utils.removeArrMatchLodash(afitxSorted, banned_arr, 'account'); + //always skip top holder as that would be actifit afitxSorted = afitxSorted.slice(1, maxAmount + 1); + let output = afitxSorted; + if (req.query.pretty){ output = '#|Token Holder | AFITX Tokens Held |
'; output += '|---|---|---|
'; @@ -1172,10 +1183,17 @@ app.get('/products', async function (req, res) { /* end point for returning current top AFIT token holders */ app.get('/topTokenHolders', async function (req, res) { var tokenHolders; + + //fetch banned accounts + let banned_users = await db.collection('banned_accounts').find({ban_status:"active"}, {fields : { user: 1, _id: 0 } }).toArray(); + //console.log(banned_users); + let banned_arr = banned_users.map(entr => entr.user); + banned_arr.push(''); + if (isNaN(req.query.count)){ - tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).toArray(); + tokenHolders = await db.collection('user_tokens').find({_id:{$nin: banned_arr}}).sort({tokens: -1}).toArray(); }else{ - tokenHolders = await db.collection('user_tokens').find().sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); + tokenHolders = await db.collection('user_tokens').find({_id:{$nin: banned_arr}}).sort({tokens: -1}).limit(parseInt(req.query.count)).toArray(); } let output = tokenHolders; if (req.query.pretty){ diff --git a/utils.js b/utils.js index 0958911..1a839a4 100644 --- a/utils.js +++ b/utils.js @@ -579,7 +579,7 @@ function format(n, c, d, t) { var s = Math.floor((ts % 60)); return padLeft(h, 2) + ':' + padLeft(m, 2) + ':' + padLeft(s, 2); } - + function toHrMn(ts) { var h = Math.floor(ts / HOURS); var m = Math.floor((ts % HOURS) / 60); @@ -816,7 +816,7 @@ async function lookupAccountPay (){ const ONE_YEAR = 365; //when is our start day: 1 is yesterday, 10 is 10 days ago - let start_days = 11; + let start_days = 15; let lookup_days = ONE_MONTH; let today = moment().utc().startOf('date').toDate() @@ -1047,6 +1047,13 @@ function sortArrLodash (arrToSort) { return _.orderBy(arrToSort, function (o) { return new Number(o.balance)},['desc']); } +function removeArrMatchLodash (arrToClean, arrToMatch, field) { + let removedEntries = _.remove(arrToClean, obj => arrToMatch.includes(obj[field])); + console.log("removedEntries"); + console.log(removedEntries); + return arrToClean; +} + async function rewardPost(post_url, vp){ //extract author and permalink from full url //check if string ends with /, remove it @@ -1146,4 +1153,5 @@ async function verifyFriendTransaction(userA, userB, tx_type, block_num, tx_id){ rewardPost: rewardPost, verifyFriendTransaction: verifyFriendTransaction, verifyGadgetTransaction: verifyGadgetTransaction, + removeArrMatchLodash: removeArrMatchLodash, } From faac33b2a540bcc107af3b1da27f32bcdd8a1d08 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Tue, 21 Jan 2020 22:41:26 +0200 Subject: [PATCH 138/193] Fix issue with extra decimal precision Fix issue with API returning longer than normal decimal point values --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 7747b8c..e625f1b 100644 --- a/app.js +++ b/app.js @@ -1970,7 +1970,7 @@ getPostRewardFunc = async function(user, url, reward_activity){ //fixing token amount display for 3 digits if (typeof post_details!= "undefined" && post_details!=null){ if (typeof post_details.token_count!= "undefined"){ - return post_details.token_count; + return parseFloat(post_details.token_count.toFixed(4)); } } //otherwise return no tokens From d76172664c0172e55ce44166a2b1eab015a14644 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:16:28 +0300 Subject: [PATCH 139/193] Implement Support Delegation Rewards Hive Adjust delegation scripting to allow supporting hive blockchain, but also provide rewards based on both hive & steem chains for both daily and weekly rewards --- delegations.js | 146 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 97 insertions(+), 49 deletions(-) diff --git a/delegations.js b/delegations.js index ec8b237..3e848e7 100644 --- a/delegations.js +++ b/delegations.js @@ -9,6 +9,8 @@ const config = utils.getConfig() const client = new dsteem.Client(config.active_node) +const hiveClient = new dsteem.Client(config.active_hive_node) + const MongoClient = require('mongodb').MongoClient const testRun = false; @@ -16,10 +18,15 @@ const testRun = false; let db let collection let bulk_delegation_entries +let bulk_hive_delegation_entries // Database Name const dbName = config.db_name -const collectionName = 'delegation_transactions' +const delegationTrxCol = 'delegation_transactions' +const hiveDelegationTrxCol = 'hive_delegation_transactions' + +const actDelgCol = 'active_delegations' +const hiveActDelgCol = 'hive_active_delegations' let properties let totalVests @@ -60,7 +67,7 @@ if (process.env.BOT_THREAD == 'MAIN'){ }); }else{ - //runRewards(true); + runRewards(true); } const SSC = require('sscjs'); @@ -450,9 +457,20 @@ function runRewards(steemOnlyReward){ db = dbClient.db(dbName) // Get the documents collection - collection = db.collection(collectionName) + collection = db.collection(delegationTrxCol) + + /**** copy a collection to another *****/ + /*let documentsToMove = db.collection(delegationTrxCol).find({}); + documentsToMove.forEach(function(doc) { + console.log('inserting'); + db.collection(hiveDelegationTrxCol).insert(doc); + }); + console.log('done'); + + return;*/ - bulk_delegation_entries = db.collection(collectionName).initializeUnorderedBulkOp(); + bulk_delegation_entries = db.collection(delegationTrxCol).initializeUnorderedBulkOp(); + bulk_hive_delegation_entries = db.collection(hiveDelegationTrxCol).initializeUnorderedBulkOp(); //updateUserTokens(); //return; @@ -528,7 +546,7 @@ async function getBenefactorPosts (account, start) { let date = moment(txs[1].timestamp).format() if (date >= start) { - console.log(txs[0]); + //console.log(txs[0]); let op = txs[1].op // Look for beneficiary payments if (op[0] === 'comment_benefactor_reward') { @@ -536,7 +554,7 @@ async function getBenefactorPosts (account, start) { console.log('---------------------------------------'); //console.log(op); let matchingAFIT = 0; - console.log(op[1]); + //console.log(op[1]); let rewardedSP = parseFloat(vestsToSteemPower(op[1].vesting_payout).toFixed(3)) console.log("rewardedSP:"+rewardedSP); //calculate dollar value @@ -660,13 +678,27 @@ async function startProcess (days, steemOnlyReward) { //if (lastTx) end = lastTx.tx_number await updateProperties() if (!testRun){ - await processDelegations(config.account, -1, end) + //update Steem delegations + console.log('>>>>>>>>>>STEEM<<<<<<<<<<<<'); + await processDelegations(client, bulk_delegation_entries, delegationTrxCol, actDelgCol, config.account, -1, end) + + //update hive delegations + console.log('>>>>>>>>>>HIVE<<<<<<<<<<<<'); + await processDelegations(hiveClient, bulk_hive_delegation_entries, hiveDelegationTrxCol, hiveActDelgCol, config.account, -1, end) } + //TEMP BREAK + //return; + + let start = moment().utc().startOf('date').subtract(days, 'days').toDate() let txEnd = moment().utc().startOf('date').toDate() if (!steemOnlyReward){ console.log('processTokenRewards'); - await processTokenRewards(start, txEnd, days) + //steem based rewards + await processTokenRewards('STEEM', client, bulk_delegation_entries, delegationTrxCol, actDelgCol, start, txEnd, days) + + //hive based rewards + await processTokenRewards('HIVE', hiveClient, bulk_hive_delegation_entries, hiveDelegationTrxCol, hiveActDelgCol, start, txEnd, days) //update our user token count post reward if (!testRun){ updateUserTokens(); @@ -677,21 +709,27 @@ async function startProcess (days, steemOnlyReward) { // Check if today is Monday, to calculate steem rewards if (dayId == 1){ //console.log('processSteemRewards'); - processSteemRewards(txEnd) + //processTokenRewards (chain, nodeLink, dbDelegLink, delTrxCol, activeDelColLink, start, end, days) { + let resSt = await processSteemRewards('STEEM', client, bulk_delegation_entries, delegationTrxCol, actDelgCol, txEnd) + //console.log('>>>>>STEEM REWARDS COMPLETE'); + let resHv = await processSteemRewards('HIVE', hiveClient, bulk_hive_delegation_entries, hiveDelegationTrxCol, hiveActDelgCol, txEnd) + //console.log('>>>>>HIVE REWARDS COMPLETE'); } } -async function processTokenRewards (start, end, days) { +async function processTokenRewards (chain, nodeLink, dbDelegLink, delTrxCol, activeDelColLink, start, end, days) { if (!start) start = moment().utc().startOf('date').subtract(days, 'days').toDate() if (!end) end = moment().utc().startOf('date').toDate() - let note = 'Delegation Reward For ' + moment(end).subtract(1, 'days').format('MMMM Do YYYY') + let note = 'Delegation Reward On ' + chain + ' For ' + moment(end).subtract(1, 'days').format('MMMM Do YYYY') - let acumulatedSteemPower = await getAcumulatedSteemPower(start, end, true); + let acumulatedSteemPower = await getAcumulatedSteemPower(nodeLink, dbDelegLink, delTrxCol, activeDelColLink, start, end, config.exclude_enabled); + + //console.log(acumulatedSteemPower.users); //handles maintaining max CAP for payments let multiplier = 1 - let currentSteemPower = await getCurrentTotalSP(end); + let currentSteemPower = await getCurrentTotalSP(activeDelColLink, end); console.log("currentSteemPower:"+currentSteemPower); //check if max CAP is reached, and apply multplier accordingly @@ -703,7 +741,7 @@ async function processTokenRewards (start, end, days) { //load list of alt accounts to reward them instead of actual delegators let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); - console.log(altAccounts); + //console.log(altAccounts); //go through all delegators, and send out AFIT rewards for (let user of acumulatedSteemPower.users) { @@ -735,13 +773,14 @@ async function processTokenRewards (start, end, days) { let reward = { user: reward_user, + chain: chain, token_count: parseFloat((user.totalSteem * multiplier).toFixed(3)), reward_activity: reward_activity, orig_account: user.user, note: note, date: end } - console.log(reward) + //console.log(reward) //only send out funds if not a test run if (!testRun){ upsertRewardTransaction(reward) @@ -749,7 +788,7 @@ async function processTokenRewards (start, end, days) { } } -async function processSteemRewards (start) { +async function processSteemRewards (chain, nodeLink, dbDelegLink, delTrxCol, activeDelColLink, start) { if (!start) start = moment().utc().startOf('date').toDate() // Get active delegations for the week console.log(config.pay_account) @@ -759,12 +798,17 @@ async function processSteemRewards (start) { //load list of alt accounts to reward them instead of actual delegators let altAccounts = await db.collection('delegation_alt_beneficiaries').find().toArray(); console.log('loading alt accounts'); - console.log(altAccounts); + //console.log(altAccounts); - Promise.all([getAcumulatedSteemPower(from, to, true), getBenefactorRewards(to, start, -1)]).then(values => { + Promise.all( + [ + getAcumulatedSteemPower(nodeLink, dbDelegLink, delTrxCol, activeDelColLink, from, to, config.exclude_enabled), //(nodeLink, dbDelegLink, delTrxCol, activeDelColLink, start, end, config.exclude_enabled); + getBenefactorRewards(to, start, -1) + ] + ).then(values => { const activeDelegations = values[0].users - console.log('***'); - console.log(values[1]); + //console.log('***'); + //console.log(values[1]); const steemRewards = values[1].split(' ')[0] const sbdRewards = values[1].split(' ')[1] const totalDelegatedSteem = values[0].totalSteem @@ -810,7 +854,7 @@ async function processSteemRewards (start) { reward.url = url*/ return reward }) - console.log(rewards) + //console.log(rewards) console.log("steem total beneficiary reward:"+steemRewards) console.log("SBD total beneficiary reward:"+sbdRewards) const data = { @@ -823,7 +867,7 @@ async function processSteemRewards (start) { var fs = require('fs'); var fileName = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); - fileName = "steemrewards"+fileName+".json"; + fileName = chain+"rewards"+fileName+".json"; console.log("fileName:"+fileName); fs.writeFile(fileName, JSON.stringify(rewards), function(err) { if(err) { @@ -851,7 +895,7 @@ function vestsToSteemPower (vests) { } -async function processDelegations (account, start, end) { +async function processDelegations (nodeLink, dbDelegLink, delTrxCol, activeDelColLink, account, start, end) { let delegationTransactions = [] let lastTrans = start let ended = false @@ -859,7 +903,7 @@ async function processDelegations (account, start, end) { console.log('Account: ' + account + ' - Start: ' + start + ' - Limit: ' + limit + ' - Last Txs: ' + end) try { // Query account history for delegations - const transactions = await client.database.call('get_account_history', [account, start, limit]) + const transactions = await nodeLink.database.call('get_account_history', [account, start, limit]) transactions.reverse() for (let txs of transactions) { //let's only fetch a max of 5 days ago delegation transactions @@ -868,7 +912,7 @@ async function processDelegations (account, start, end) { //today let start = moment().utc().startOf('date').toDate() - let to = moment(start).subtract(5, 'days').toDate() + let to = moment(start).subtract(6, 'days').toDate() let end = moment(to).format() if (txs[0] === end || tx_date < end) { @@ -889,7 +933,7 @@ async function processDelegations (account, start, end) { data.tx_date = new Date(txs[1].timestamp) delegationTransactions.push(data) - bulk_delegation_entries.find( + dbDelegLink.find( { delegator: data.delegator, vesting_shares: data.vesting_shares, @@ -899,19 +943,20 @@ async function processDelegations (account, start, end) { // Insert new transactions and update active ones if (delegationTransactions.length > 0) { try{ - await bulk_delegation_entries.execute(); + await dbDelegLink.execute(); }catch(bulkerr){ utils.log(bulkerr); } - //await collection.insert(delegationTransactions) - await updateActiveDelegations() + //update relevant delegations collection + await updateActiveDelegations(delTrxCol, activeDelColLink) + } else { console.log('--- No new delegations ---') return; } // If more pending delegations call process againg with new index if (start !== limit && !ended){ - return processDelegations(account, lastTrans, end) + return processDelegations(nodeLink, dbDelegLink, delTrxCol, activeDelColLink, account, lastTrans, end) } // console.log(transactions) return; @@ -919,7 +964,7 @@ async function processDelegations (account, start, end) { console.log(err) // Consider exponential backoff if extreme cases start happening if (err.type === 'request-timeout' || err.type === 'body-timeout'){ - return processDelegations(account, start, end); + return processDelegations(nodeLink, dbDelegLink, delTrxCol, activeDelColLink, account, start, end); } } } @@ -944,7 +989,7 @@ async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { let op = txs[1].op // Look for delegation operations if (op[0] === 'comment_benefactor_reward') { - console.log(op[1]); + //console.log(op[1]); //SP is the sum of conversting vesting payout to SP, and appending any STEEM payouts let newSp = vestsToSteemPower(op[1].vesting_payout) + parseFloat(op[1].steem_payout.split(' ')[0]) totalSp = totalSp + newSp @@ -964,10 +1009,10 @@ async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { return +totalSp.toFixed(3)+' ' +totalSBD.toFixed(3) } -async function getActiveDelegations (start, excludeOn) { +async function getActiveDelegations (delTrxCol, start, excludeOn) { start = new Date(start) if (excludeOn){ - return collection.aggregate( + return db.collection(delTrxCol).aggregate( [ { $match: { 'tx_date': { '$lte': start } } }, { $sort: { delegator: 1, tx_date: 1 } }, @@ -993,7 +1038,7 @@ async function getActiveDelegations (start, excludeOn) { ] ).toArray() }else{ - return collection.aggregate( + return db.collection(delTrxCol).aggregate( [ { $match: { 'tx_date': { '$lte': start } } }, { $sort: { delegator: 1, tx_date: 1 } }, @@ -1026,10 +1071,9 @@ async function getActiveDelegations (start, excludeOn) { * params: toDate - date before which all current SP is calculated * returns: total value of current SP count up to passed date */ -async function getCurrentTotalSP(toDate){ +async function getCurrentTotalSP(actDelgCol, toDate){ toDate = moment(toDate).toDate() - var actDelgCol = 'active_delegations'; //perform an aggregation based on max date, exluded delegators, and return back sum of SP and delegator count (we only need for now totalSP) var results = await db.collection(actDelgCol).aggregate([ { @@ -1050,34 +1094,38 @@ async function getCurrentTotalSP(toDate){ ]).toArray(); //function(err, results) { //var output = 'tokens distributed:'+results[0].totalSP; - console.log(results); + //console.log(results); return results[0].totalSP; //}); } -async function getAcumulatedSteemPower (from, to, excludeOn) { +async function getAcumulatedSteemPower (nodeLink, dbDelegLink, delTrxCol, activeDelColLink, from, to, excludeOn) { let result = { users: [] } let totalSteem = 0 from = moment(from).toDate() to = moment(to).toDate() + //console.log('get Acc Power'); + //console.log(activeDelColLink); // Get active delegations for the week - let activeDelegations = await getActiveDelegations(from, excludeOn) + let activeDelegations = await getActiveDelegations(delTrxCol, from, excludeOn) + //console.log(activeDelegations); // Get transactions of the processed week let weekTxs if (excludeOn){ console.log('excluding users'); - weekTxs = await db.collection('delegation_transactions').find( + weekTxs = await db.collection(delTrxCol).find( {'tx_date': {$gt: from, $lt: to}, 'delegator': {$nin: config.exclude_rewards}}) .sort({tx_date: 1}).toArray() }else{ console.log('no exclude'); - weekTxs = await db.collection('delegation_transactions').find( + weekTxs = await db.collection(delTrxCol).find( {'tx_date': {$gt: from, $lt: to}}) .sort({tx_date: 1}).toArray() } + //console.log(weekTxs); let allTxs = activeDelegations.concat(weekTxs) let groupedTxs = _.groupBy(allTxs, 'delegator') for (let index in groupedTxs) { @@ -1103,9 +1151,9 @@ async function getAcumulatedSteemPower (from, to, excludeOn) { return result } -async function updateActiveDelegations () { +async function updateActiveDelegations (delgTrxCol, targetCol) { console.log('--- Updating active delegations ---') - let query = collection.aggregate( + let query = db.collection(delgTrxCol).aggregate( [ { $sort: { delegator: 1, tx_date: 1 } }, { @@ -1121,15 +1169,15 @@ async function updateActiveDelegations () { ] ) let activeDelegations = await query.toArray() - await db.collection('active_delegations').drop() - await db.collection('active_delegations').insert(activeDelegations) - console.log('done updating delegations'); + await db.collection(targetCol).drop() + await db.collection(targetCol).insert(activeDelegations) + console.log('done updating delegations '+targetCol); return ; } function upsertRewardTransaction (reward) { return db.collection('token_transactions').update( - { user: reward.user, date: reward.date, reward_activity: reward.reward_activity, orig_account: reward.orig_account }, + { user: reward.user, chain: reward.chain, date: reward.date, reward_activity: reward.reward_activity, orig_account: reward.orig_account }, reward, { upsert: true } ) @@ -1210,4 +1258,4 @@ async function claimRewards(){ }else{ console.log('no rewards to claim for now'); } -} +} \ No newline at end of file From 603f939e437e9ad8749b071f37ea8355830fc4ca Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:20:27 +0300 Subject: [PATCH 140/193] Implement Support User Key Login Implement support for proper login verification for users, key-based, encrypted --- app.js | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 269 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index e625f1b..0196670 100644 --- a/app.js +++ b/app.js @@ -48,6 +48,9 @@ MongoClient.connect(url, function(err, client) { // Get the documents collection collection = db.collection(collection_name); + + disableUserLogin(); + } else { utils.log(err, 'api'); } @@ -71,6 +74,8 @@ let scJob = schedule.scheduleJob('*/5 * * * *', async function(){ //reset array //usersAFITXBal = []; fetchAFITXBal(0); + + disableUserLogin(); }); //allows setting acceptable origins to be included across all function calls @@ -80,6 +85,7 @@ app.use(function(req, res, next) { if(allowedOrigins.indexOf(origin) > -1){ res.setHeader('Access-Control-Allow-Origin', origin); } + res.setHeader('Access-Control-Allow-Headers', 'Origin, Content-Type, x-acti-token'); return next(); }); @@ -104,10 +110,22 @@ let accountRefresh = false; let accountQueries = 0; loadAccountData(); -async function loadAccountData(){ +async function disableUserLogin(){ + console.log('check outdated logins'); + let db_col = db.collection('user_login_token'); + let dateTarget = new Date(); + //allow logins to remain valid for 4 hours + dateTarget.setHours(dateTarget.getHours()-4); + console.log(dateTarget); + //find existing login entry in DB to override + let result = await db_col.remove({lastlogin: {$lt: dateTarget }}); + //console.log(result); +} + +async function loadAccountData(bchain){ //load main account data - account = await utils.getAccountData(config.account); + account = await utils.getAccountData(config.account, bchain); } async function fetchAFITXBal(offset){ @@ -190,6 +208,33 @@ generatePassword = function (multip) { }; + +var bodyParser = require('body-parser'); +app.use(bodyParser.json()); // support json encoded bodies +app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies + +//const key = crypto.randomBytes(32); +//const iv = crypto.randomBytes(16); + +function encrypt(text) { + let cipher = crypto.createCipheriv(config.ppkey_enc_mode, config.user_ppkey_db, config.user_ppkey_iv); + let encrypted = cipher.update(text); + encrypted = Buffer.concat([encrypted, cipher.final()]); + //return { iv: iv.toString('hex'), encryptedData: encrypted.toString('hex') }; + return encrypted.toString('hex'); +} + +function decrypt(text) { + //let iv = Buffer.from(text.iv, 'hex'); + //let encryptedText = Buffer.from(text.encryptedData, 'hex'); + let encryptedText = Buffer.from(text, 'hex'); + let decipher = crypto.createDecipheriv(config.ppkey_enc_mode, config.user_ppkey_db, config.user_ppkey_iv); + let decrypted = decipher.update(encryptedText); + decrypted = Buffer.concat([decrypted, decipher.final()]); + return decrypted.toString(); +} + + app.get('/votingStatus', async function (req, res) { let votingStatus = await db.collection('voting_status').findOne({}); accountQueries += 1; @@ -210,6 +255,228 @@ app.get('/votingStatus', async function (req, res) { res.send({'status': votingStatus, 'vp': vp_res, 'reward_start': reward_start}); }); + +let jwt = require('jsonwebtoken'); + +//function ensures user is properly logged in +let checkHdrs = (req, res, next) => { + let token = req.headers['x-acti-token'] || req.headers['authorization']; // Express headers are auto converted to lowercase + + if (token) { + if (token.startsWith('Bearer ')) { + // Remove Bearer from string + token = token.slice(7, token.length); + } + req.query.token = token; + jwt.verify(token, config.secret, async (err, decoded) => { + if (err) { + return res.json({ + success: false, + message: 'Token is not valid' + }); + } else { + let user; + + if (req.query && req.query.user){ + user = req.query.user; + }else{ + res.send({error: 'user not supplied'}); + } + + //console.log(operation[1].required_posting_auths); + //console.log(req.query.token); + + //check if user is validated with stored encrypted posting key + let db_col = db.collection('user_login_token'); + //find existing login entry in DB + let user_tkn = await db_col.findOne({user: user, token: req.query.token}); + //console.log(user_tkn); + if (!user_tkn || !user_tkn.ppkey){ + console.error('Authentication failed. Key not found'); + res.send({error: 'Authentication failed. Key not found'}); + return; + } + req.ppkey = user_tkn.ppkey; + req.decoded = decoded; + next(); + } + }); + } else { + return res.json({ + success: false, + message: 'Auth token is not supplied' + }); + } +}; + + +app.get('/performTrx', checkHdrs, async function (req, res) { + console.log('>>performTrx'); + + + const receivedPlaintext = decrypt(req.ppkey); + + //set HIVE as default + let bchain = 'HIVE'; + + let userKey = receivedPlaintext; + + let operation; + console.log(req.query.operation); + if (req.query && req.query.operation){ + operation = JSON.parse(req.query.operation); + //operation = req.query.operation; + }else{ + res.send({error: 'operation not supplied'}); + } + + if (req.query.bchain){ + bchain = req.query.bchain; + } + + let match_arr = Object.entries(operation); + /*console.log(user); + console.log(operation); + console.log((typeof operation)); + console.log(match_arr); + console.log(match_arr[0][1]);*/ + + //perform transaction + let performTrx = await utils.processSteemTrx(match_arr[0][1], userKey, bchain); + console.log(performTrx); + res.send({success: true, trx: performTrx}); +}); + +app.get('/fetchUserData', checkHdrs, async function (req, res) { + //validate proper data used + let username; + if (req.query && req.query.user){ + username = req.query.user; + } + let bchain = 'HIVE'; + if (req.query && req.query.bchain){ + bchain = req.query.bchain; + } + console.log('>>>>fetchuserdata'); + console.log(bchain); + //console.log(username); + //console.log(req.ppkey); + const receivedPlaintext = decrypt(req.ppkey); + let isValidUser = await utils.validateAccountLogin(username, receivedPlaintext, bchain); + + console.log('isValidUser'); + console.log(isValidUser); + //if (username === mockedUsername && ppkey === mockedPpkey) { + if (isValidUser.result){ + res.json({ + success: true, + userdata: isValidUser.account + }); + } else { + res.status(403).send({ + success: false, + message: 'Invalid user' + }); + } +}); + + +app.get('/updateSettings/', checkHdrs, async function (req, res) { + let newSettings; + if (req.query && req.query.user && req.query.settings){ + newSettings = JSON.parse(req.query.settings); + }else{ + res.send({error:'invalid request'}) + return; + } + console.log(newSettings); + try{ + let setgs = await db.collection('user_settings').replaceOne({user: req.query.user}, {user: req.query.user, settings: newSettings}, {upsert : true }); + //console.log(setgs); + res.send({success: true}); + }catch(err){ + res.send({error: true}); + } +}); + + +app.get('/userSettings/:user', async function (req, res) { + let setgs = await db.collection('user_settings').findOne({user: req.params.user}, {fields : { _id:0} }); + console.log(setgs); + res.send(setgs); +}); + + +app.post('/loginAuth', async function (req, res) { + console.log('login'); + let username = null; + if (req.body && req.body.username){ + username = req.body.username; + } + let ppkey = null; + if (req.body && req.body.ppkey){ + ppkey = req.body.ppkey; + } + + + if (username && ppkey) { + let db_col = db.collection('user_login_token'); + //find existing login entry in DB to override + let user_tkn = await db_col.findOne({user: username}); + //console.log(user_tkn); + + //encode ppkey + const ciphertext = encrypt(ppkey); + + let bchain = 'HIVE'; + if (req.body && req.body.bchain){ + bchain = req.body.bchain; + } + + //validate proper data used + let isValidUser = await utils.validateAccountLogin(username, ppkey, bchain); + console.log('isValidUser'); + //console.log(isValidUser); + //if (username === mockedUsername && ppkey === mockedPpkey) { + if (isValidUser.result){ + let token = jwt.sign({username: username}, + config.secret, + { expiresIn: '24h' // expires in 24 hours + } + ); + //save to DB + //save encrypted version + token + if (!user_tkn){ + user_tkn = new Object(); + } + user_tkn.user = username; + user_tkn.token = token; + user_tkn.ppkey = ciphertext; + user_tkn.lastlogin = new Date(); + + let db_save = await db_col.save(user_tkn); + + // return the JWT token for the future API calls + res.json({ + success: true, + message: 'Authentication successful!', + token: token, + userdata: isValidUser.account + }); + } else { + res.status(403).send({ + success: false, + message: 'Incorrect username or ppkey' + }); + } + } else { + res.status(400).send({ + success: false, + message: 'Authentication failed! Please check the request' + }); + } +}); + /* end point for user total token count display */ app.get('/user/:user', async function (req, res) { let user = await grabUserTokensFunc(req.params.user); From 21a1f08877f981f6c84ed13734e7b97bef65abd5 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:23:41 +0300 Subject: [PATCH 141/193] Support Multi-Chain Transactions Append support for multi-chain (hive+steem) transactions Allow account creation, delegation and other trx on both chains, and using HIVE in addition to STEEM for payments/delegations. Adjust friendship requests to function on both chains, depending on user's choice --- app.js | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/app.js b/app.js index 0196670..c3f767b 100644 --- a/app.js +++ b/app.js @@ -242,10 +242,11 @@ app.get('/votingStatus', async function (req, res) { accountQueries = 0; accountRefresh = true; } + let bchain = (req.query&&req.query.bchain?req.query.bchain:''); //fetch anew account data if account is empty or we need to refresh account data if (!account || accountRefresh){ console.log('refreshing account data'); - account = await utils.getAccountData(config.account); + account = await utils.getAccountData(config.account, bchain); accountRefresh = false; } let vp_res = await utils.getVotingPower(account); @@ -734,7 +735,8 @@ app.get('/modAction', async function (req, res) { res.send({'error': 'VP needs to be numeric'}); return; } - result = await utils.rewardPost(modTrans.fullurl, modTrans.vp) + let bchain = (req.query&&req.query.bchain?req.query.bchain:''); + result = await utils.rewardPost(modTrans.fullurl, modTrans.vp, bchain) console.log(result); result.status='success'; break; @@ -1113,9 +1115,9 @@ app.get('/userFriendRequests/:user', async function (req, res) { }); /* end point for adding user's friend */ -app.get('/addFriend/:userA/:userB/:blockNo/:trxID', async function (req, res) { +app.get('/addFriend/:userA/:userB/:blockNo/:trxID/:bchain', async function (req, res) { //ensure proper transaction - let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'add-friend-request', req.params.blockNo, req.params.trxID); + let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'add-friend-request', req.params.blockNo, req.params.trxID, req.params.bchain); if (!ver_trx){ res.send({status: 'error'}); return; @@ -1145,9 +1147,9 @@ app.get('/addFriend/:userA/:userB/:blockNo/:trxID', async function (req, res) { /* end point for cancelling friend request */ -app.get('/cancelFriendRequest/:userA/:userB/:blockNo/:trxID', async function (req, res) { +app.get('/cancelFriendRequest/:userA/:userB/:blockNo/:trxID/:bchain', async function (req, res) { //ensure proper transaction - let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'cancel-friend-request', req.params.blockNo, req.params.trxID); + let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'cancel-friend-request', req.params.blockNo, req.params.trxID, req.params.bchain); if (!ver_trx){ res.send({status: 'error'}); return; @@ -1179,9 +1181,9 @@ app.get('/cancelFriendRequest/:userA/:userB/:blockNo/:trxID', async function (re /* end point for cancelling friend request */ -app.get('/acceptFriend/:userA/:userB/:blockNo/:trxID', async function (req, res) { +app.get('/acceptFriend/:userA/:userB/:blockNo/:trxID/:bchain', async function (req, res) { //ensure proper transaction - let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'accept-friendship', req.params.blockNo, req.params.trxID); + let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'accept-friendship', req.params.blockNo, req.params.trxID, req.params.bchain); if (!ver_trx){ res.send({status: 'error'}); return; @@ -1234,9 +1236,9 @@ app.get('/acceptFriend/:userA/:userB/:blockNo/:trxID', async function (req, res) /* end point for dropping friendship */ -app.get('/dropFriendship/:userA/:userB/:blockNo/:trxID', async function (req, res) { +app.get('/dropFriendship/:userA/:userB/:blockNo/:trxID/:bchain', async function (req, res) { //ensure proper transaction - let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'cancel-friendship', req.params.blockNo, req.params.trxID); + let ver_trx = await utils.verifyFriendTransaction(req.params.userA, req.params.userB, 'cancel-friendship', req.params.blockNo, req.params.trxID, req.params.bchain); if (!ver_trx){ res.send({status: 'error'}); return; @@ -2467,7 +2469,7 @@ proceedAccountCreation = async function (req){ //let's create the account now let accountCreated = false; let transStored = false; - accountCreated = await utils.createAccount(req.query.new_account, req.query.new_pass); + accountCreated = await utils.createAccount(req.query.new_account, req.query.new_pass, req.query.cur_bchain); if (accountCreated){ transStored = await storeSignupTransaction(req); //proceed only if a proper referrer was sent @@ -3299,7 +3301,7 @@ app.get('/confirmPayment', async function(req,res){ accountCreated = await claimAndCreateAccount(req); //only delegate if account created and delegation is enabled if (accountCreated && promo_match.delegation){ - delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate); + delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate, req.query.cur_bchain); } //decrease number of permitted entries @@ -3328,7 +3330,16 @@ app.get('/confirmPayment', async function(req,res){ let memo_used = await db.collection('signup_transactions').findOne({memo: req.query.memo}); console.log('memo_used:'+memo_used); if (typeof memo_used == "undefined" || memo_used == null){ - paymentReceivedTx = await utils.confirmPaymentReceived(req); + //check on which blockchain transaction was sent based on currency + let bchain = (req.query&&req.query.bchain?req.query.bchain:''); + if (req.query.sent_cur){ + if (req.query.sent_cur == 'STEEM' || req.query.sent_cur == 'SBD'){ + bchain = 'STEEM'; + }else if (req.query.sent_cur == 'HIVE' || req.query.sent_cur == 'HBD'){ + bchain = 'HIVE'; + } + } + paymentReceivedTx = await utils.confirmPaymentReceived(req, bchain); console.log('>>>> got TX '+paymentReceivedTx); if (paymentReceivedTx != ''){ req.query.confirming_tx = paymentReceivedTx; @@ -3336,7 +3347,7 @@ app.get('/confirmPayment', async function(req,res){ try{ accountCreated = await claimAndCreateAccount(req); if (accountCreated){ - delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate); + delegationSuccess = await utils.delegateToAccount(req.query.new_account, spToDelegate, req.query.bchain); } }catch(e){ console.log(e); @@ -3366,7 +3377,7 @@ claimAndCreateAccount = async function (req){ console.log('Current RC: ' + utils.format(results.estimated_pct) + '% '); if (results.estimated_pct>50){ //if we reached min threshold, claim more spots for discounted accounts - accountClaimed = await utils.claimDiscountedAccount(); + accountClaimed = await utils.claimDiscountedAccount(req.query.cur_bchain); } }catch(err){ console.log('error grabbing RC'); @@ -3499,7 +3510,8 @@ app.get('/confirmPaymentPasswordVerify', async function(req,res){ res.write(' '); },8000); try{ - paymentReceivedTx = await utils.confirmPaymentReceivedPassword(req, config.signup_account); + let bchain = (req.query&&req.query.bchain?req.query.bchain:''); + paymentReceivedTx = await utils.confirmPaymentReceivedPassword(req, bchain); console.log('>>>> got TX '+paymentReceivedTx); if (paymentReceivedTx != ''){ try{ @@ -3544,7 +3556,8 @@ app.get('/confirmBuyAction', async function(req,res){ res.write(' '); },8000); try{ - match_trx = await utils.confirmPaymentReceivedBuy(req, config.signup_account); + let bchain = (req.query&&req.query.bchain?req.query.bchain:''); + match_trx = await utils.confirmPaymentReceivedBuy(req, bchain); console.log('>>>> got TX '+match_trx); let targetUser = req.query.from; if (match_trx != ''){ From a9ddf31447940e90aed50f29af7c0797a582af54 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:25:12 +0300 Subject: [PATCH 142/193] Sorting Capability for Trx By Type List Add Sorting Capability for Trx By Type List Add Posts By Tag Display --- app.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index c3f767b..eac2619 100644 --- a/app.js +++ b/app.js @@ -498,15 +498,29 @@ app.get('/transactions/:user?', async function (req, res) { res.send(transactions); }); +app.get('/postsbytag/:tag', async function (req, res) { + let posts = {}; + if(req.params.tag){ + let query = {"json_metadata.tags": {$all: [req.params.tag]}}; + posts = await db.collection('verified_posts').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + } + res.send(posts); +}); + /* end point for transactions display by type (limited by 1000) */ app.get('/transactionsByType/', async function (req, res) { let query = {}; let transactions = {}; let proceed = false; + let dateSort = 1; if (req.query.type){ proceed = true; query = {reward_activity: req.query.type} + } + if (req.query.datesort){ + dateSort = parseInt(req.query.datesort) + } let startDate = ''; let endDate = ''; @@ -529,7 +543,7 @@ app.get('/transactionsByType/', async function (req, res) { } console.log(query); if (proceed){ - transactions = await db.collection('token_transactions').find(query).sort({date: 1}).limit(1000).toArray(); + transactions = await db.collection('token_transactions').find(query).sort({date: dateSort}).limit(1000).toArray(); } res.send(transactions); }); From debfaaf55b4c194d1b8033ef726595d279283c27 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:31:16 +0300 Subject: [PATCH 143/193] Adjustments Supporting Multi-Chain + Login Implement Adjustments to utils functionality to support login using keys + multi-chains (hive & steem) --- utils.js | 404 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 302 insertions(+), 102 deletions(-) diff --git a/utils.js b/utils.js index 1a839a4..aed3fa2 100644 --- a/utils.js +++ b/utils.js @@ -1,20 +1,21 @@ var fs = require("fs"); const steem = require('steem'); + var _ = require('lodash'); const axios = require('axios'); const dsteem = require('dsteem'); const moment = require('moment') -const steem_node = 'https://api.steemit.com';//'https://steemd.minnowsupportproject.org';//'https://api.steem.house';// getConfig(); const client = new dsteem.Client(config.active_node); +const hiveClient = new dsteem.Client(config.active_hive_node); var config; let th_id = -1; -steem.api.setOptions({ url: steem_node }); +steem.api.setOptions({ url: config.active_node }); var STEEMIT_100_PERCENT = 10000; var STEEMIT_VOTE_REGENERATION_SECONDS = (5 * 60 * 60 * 24); @@ -33,9 +34,21 @@ var HOURS = 60 * 60; let properties let totalVests let totalSteem + let hiveProps + let totalHive + let totalHiveVests + + function setProperNode(bchain){ + if (bchain == "STEEM"){ + steem.api.setOptions({ url: config.active_node }); + }else{ + steem.api.setOptions({ url: config.active_hive_node }); + } + } - async function getAccountData(account_name){ + async function getAccountData(account_name, bchain){ let account = null; + await setProperNode(bchain); //attempt to load account data try{ let account_res = await steem.api.getAccountsAsync([config.account]); @@ -46,7 +59,43 @@ var HOURS = 60 * 60; return account; } - function updateSteemVariables() { + async function validateAccountLogin(username, priv_pkey, bchain){ + await setProperNode(bchain); + console.log('validateAccountLogin'); + let account_res = await steem.api.getAccountsAsync([username]); + console.log(account_res[0]); + let pub_pkey = account_res[0].posting.key_auths[0][0]; + try{ + let res = await steem.auth.wifIsValid(priv_pkey, pub_pkey); + //console.log(res); + return {result: res, account: account_res[0]}; + }catch(err){ + console.log(err); + return {result:false}; + } + } + + async function processSteemTrx(operation, userKey, bchain){ + console.log('utils processSteemTrx'); + console.log(operation); + const ops = [ operation ]; + console.log('>>>>>>>>>>>> selected bchain'); + console.log(bchain); + await setProperNode(bchain); + let tx = await steem.broadcast.sendAsync( + { operations: ops, extensions: [] }, + { posting: userKey } + ).catch(err => { + console.log(err.message); + return {error: err.message}; + }); + + console.log(tx); + return {tx: tx}; + } + + function updateSteemVariables(bchain) { + setProperNode(bchain); steem.api.getRewardFund("post", function (e, t) { console.log(e,t); rewardBalance = parseFloat(t.reward_balance.replace(" STEEM", "")); @@ -65,7 +114,7 @@ var HOURS = 60 * 60; console.log(sbd_print_percentage); }); - setTimeout(updateSteemVariables, 180 * 1000) + setTimeout(updateSteemVariables, 180 * 1000, bchain) } // updateSteemVariables(); @@ -102,7 +151,7 @@ var HOURS = 60 * 60; var data={"jsonrpc":"2.0","id":1,"method":"condenser_api.get_account_count","params":{}}; //return new Promise(function(fulfill,reject){ //var request = require("request"); - let location = steem_node; + let location = config.active_node; var response = await axios.post(location, {"jsonrpc":"2.0","id":1,"method":"rc_api.find_rc_accounts","params":{"accounts":[account_name]}}); console.log(response.data.result.rc_accounts); @@ -171,10 +220,11 @@ var HOURS = 60 * 60; } //function handles confirming if payment was received - async function confirmPaymentReceived (req) { + async function confirmPaymentReceived (req, bchain) { getConfig(); return new Promise((resolve, reject) => { th_id = setInterval(async function(){ + await setProperNode(bchain); console.log('check funds'); steem.api.getAccountHistory(config.signup_account, -1, 3000, (err, transactions) => { let tx_id = ''; @@ -185,7 +235,11 @@ var HOURS = 60 * 60; //if we found a transfer operation sent to our target account, with the correct memo and the proper amount, proceed if (op[0] === 'transfer'){ let sentAmount = op[1].amount.split(' ')[0]; - if (op[1].to === config.signup_account && op[1].memo === req.query.memo && sentAmount >= (parseFloat(req.query.steem_invest)-0.1)){ + let sentCur = op[1].amount.split(' ')[1]; + if (op[1].to === config.signup_account + && op[1].memo === req.query.memo + && sentAmount >= (parseFloat(req.query.steem_invest)-0.1) + && sentCur === req.query.sent_cur){ console.log('in'); console.log(op[1]); @@ -214,12 +268,14 @@ var HOURS = 60 * 60; } //function handles confirming if payment was received - async function confirmPaymentReceivedPassword (req) { + async function confirmPaymentReceivedPassword (req, bchain) { getConfig(); console.log('confirmPaymentReceivedPassword'); return new Promise((resolve, reject) => { let th_id = setInterval(async function(){ console.log('check funds'); + console.log(bchain); + await setProperNode(bchain); steem.api.getAccountHistory(config.exchange_account, -1, 300, (err, transactions) => { let tx_id = ''; let paymentFound = false; @@ -228,6 +284,8 @@ var HOURS = 60 * 60; //check if we received a transfer to our target account //if we found a transfer operation sent to our target account, with the correct memo and the proper amount, proceed if (op[0] === 'transfer'){ + //console.log('transfer op '); + //console.log(op[1]); let sentAmount = op[1].amount.split(' ')[0]; if (op[1].to === config.exchange_account && op[1].from === req.query.from && sentAmount >= 1){ console.log('in'); @@ -258,12 +316,13 @@ var HOURS = 60 * 60; } //function handles confirming if payment was received - async function confirmPaymentReceivedBuy (req) { + async function confirmPaymentReceivedBuy (req, bchain) { getConfig(); console.log('confirmPaymentReceivedBuy'); return new Promise((resolve, reject) => { let th_id = setInterval(async function(){ console.log('check buy funds'); + await setProperNode(bchain); steem.api.getAccountHistory(config.buy_account, -1, 800, (err, transactions) => { let tx_id = ''; let paymentFound = false; @@ -305,7 +364,7 @@ var HOURS = 60 * 60; //function handles claiming spots for accounts - async function claimDiscountedAccount(){ + async function claimDiscountedAccount(chain){ console.log('claimDiscountedAccount'); if (typeof config == 'undefined' || config == null){ getConfig(); @@ -323,30 +382,60 @@ var HOURS = 60 * 60; config.active_key ); let result = ''; - try{ - result = await client.broadcast.sendOperations(ops, privateKey); - console.log('success'); - return true; - }catch(err){ - console.log(err); - return false; + let outcSteem = false; + let outcHive = false; + if (!chain || chain == 'STEEM'){ + + try{ + result = await client.broadcast.sendOperations(ops, privateKey); + console.log('success'); + outcSteem = true; + }catch(err){ + console.log(err); + outcSteem = false; + } + } + if (!chain || chain == 'HIVE'){ + try{ + result = await hiveClient.broadcast.sendOperations(ops, privateKey); + console.log('success'); + outcHive = true; + }catch(err){ + console.log(err); + outcHive = false; + } } + return (outcSteem || outcHive); } //function handles creating accounts via discounted claimed spots or normal paid method - async function createAccount (username, password){ + async function createAccount (username, password, chain){ if (typeof config == 'undefined' || config == null){ getConfig(); } - //check if account exists - const _account = await client.database.call('get_accounts', [[username]]); - //account not available to register - if (_account.length>0) { - console.log('account already exists'); - console.log(_account); - return false; + + if (!chain || chain == 'STEEM'){ + //check if account exists + const _account = await client.database.call('get_accounts', [[username]]); + //account not available to register + if (_account.length>0) { + console.log('account already exists'); + console.log(_account); + return false; + } } - + + if (!chain || chain == 'HIVE'){ + //check if account exists + const _account = await hiveClient.database.call('get_accounts', [[username]]); + //account not available to register + if (_account.length>0) { + console.log('account already exists'); + console.log(_account); + return false; + } + } + console.log('account available'); //create keys for new account @@ -374,95 +463,187 @@ var HOURS = 60 * 60; //container for required ops let ops = []; - + let hiveOps = []; //if we have discounted accounts still available, let's do that, otherwise let's pay for account let creator = config.account; - const _creator_account = await client.database.call('get_accounts', [ - [creator], - ]); - console.log('current pending claimed accounts: ' + _creator_account[0].pending_claimed_accounts); + let steemAccountSuccess = false; + let hiveAccountSuccess = false; - if (_creator_account[0].pending_claimed_accounts > 0) { - - //the create discounted account operation - const create_op = [ - 'create_claimed_account', - { - creator: creator, - new_account_name: username, - owner: ownerAuth, - active: activeAuth, - posting: postingAuth, - memo_key: memoKey, - json_metadata: '', - extensions: [], - } - ]; - ops.push(create_op); - }else{ - - const create_op = [ - 'account_create', - { - fee: '3.000 STEEM', - creator: creator, - new_account_name: username, - owner: ownerAuth, - active: activeAuth, - posting: postingAuth, - memo_key: memoKey, - json_metadata: '', - extensions: [], - } - ]; - ops.push(create_op); + if (!chain || chain == 'STEEM'){ + const _creator_account = await client.database.call('get_accounts', [ + [creator], + ]); + console.log('current pending claimed accounts: ' + _creator_account[0].pending_claimed_accounts); + + if (_creator_account[0].pending_claimed_accounts > 0) { + + //the create discounted account operation + const create_op = [ + 'create_claimed_account', + { + creator: creator, + new_account_name: username, + owner: ownerAuth, + active: activeAuth, + posting: postingAuth, + memo_key: memoKey, + json_metadata: '', + extensions: [], + } + ]; + ops.push(create_op); + }else{ + + const create_op = [ + 'account_create', + { + fee: '3.000 STEEM', + creator: creator, + new_account_name: username, + owner: ownerAuth, + active: activeAuth, + posting: postingAuth, + memo_key: memoKey, + json_metadata: '', + extensions: [], + } + ]; + ops.push(create_op); + } + + const privateKey = dsteem.PrivateKey.fromString(config.active_key); + //proceed executing the selected operation(s) + let result = ''; + try{ + result = await client.broadcast.sendOperations(ops, privateKey); + console.log('success'); + steemAccountSuccess = true; + }catch(err){ + console.log(err); + steemAccountSuccess = false; + } } - const privateKey = dsteem.PrivateKey.fromString(config.active_key); - //proceed executing the selected operation(s) - let result = ''; - try{ - result = await client.broadcast.sendOperations(ops, privateKey); - console.log('success'); - return true; - }catch(err){ - console.log(err); - return false; + if (!chain || chain == 'HIVE'){ + const _creator_account = await hiveClient.database.call('get_accounts', [ + [creator], + ]); + console.log('current pending claimed accounts: ' + _creator_account[0].pending_claimed_accounts); + + if (_creator_account[0].pending_claimed_accounts > 0) { + + //the create discounted account operation + const create_op = [ + 'create_claimed_account', + { + creator: creator, + new_account_name: username, + owner: ownerAuth, + active: activeAuth, + posting: postingAuth, + memo_key: memoKey, + json_metadata: '', + extensions: [], + } + ]; + hiveOps.push(create_op); + }else{ + + const create_op = [ + 'account_create', + { + fee: '3.000 STEEM', + creator: creator, + new_account_name: username, + owner: ownerAuth, + active: activeAuth, + posting: postingAuth, + memo_key: memoKey, + json_metadata: '', + extensions: [], + } + ]; + hiveOps.push(create_op); + } + + const privateKey = dsteem.PrivateKey.fromString(config.active_key); + //proceed executing the selected operation(s) + let result = ''; + try{ + result = await hiveClient.broadcast.sendOperations(hiveOps, privateKey); + console.log('success'); + hiveAccountSuccess = true; + }catch(err){ + console.log(err); + hiveAccountSuccess = false; + } } + return (steemAccountSuccess || hiveAccountSuccess); } //function handles delegating to a specific account - async function delegateToAccount (delegatee, steemPowerAmount){ + async function delegateToAccount (delegatee, steemPowerAmount, chain){ if (typeof config == 'undefined' || config == null){ getConfig(); } const privateKey = dsteem.PrivateKey.fromString( config.full_pay_ac_key ); - //grab matching amount of Vests to delegate - let matchingVests = await steemPowerToVests(steemPowerAmount); - console.log('matchingVests:'+matchingVests); - const op = [ - 'delegate_vesting_shares', - { - delegator: config.full_pay_benef_account, - delegatee: delegatee, - vesting_shares: matchingVests+' VESTS', - }, - ]; + let result = ''; - try{ - result = await client.broadcast.sendOperations([op], privateKey); - console.log('Included in block:'+ result.block_num); - console.log('returning back'); - return true; - }catch(err){ - console.log(err); - console.log('returning back err'); - return false; + let steemDg = false; + let hiveDg = false; + if (!chain || chain == 'STEEM'){ + try{ + //grab matching amount of Vests to delegate + let matchingVests = await steemPowerToVests(steemPowerAmount); + console.log('matchingVests:'+matchingVests); + const op = [ + 'delegate_vesting_shares', + { + delegator: config.full_pay_benef_account, + delegatee: delegatee, + vesting_shares: matchingVests+' VESTS', + }, + ]; + + result = await client.broadcast.sendOperations([op], privateKey); + console.log('Included in block:'+ result.block_num); + console.log('returning back'); + steemDg = true; + }catch(err){ + console.log(err); + console.log('returning back err'); + steemDg = false; + } + } + if (!chain || chain == 'HIVE'){ + try{ + //grab matching amount of Vests to delegate + let matchingHiveVests = await hivePowerToVests(steemPowerAmount); + console.log('matchingHiveVests:'+matchingHiveVests); + const op = [ + 'delegate_vesting_shares', + { + delegator: config.full_pay_benef_account, + delegatee: delegatee, + vesting_shares: matchingHiveVests+' VESTS', + }, + ]; + + result = await hiveClient.broadcast.sendOperations([op], privateKey); + console.log('Included in block:'+ result.block_num); + console.log('returning back'); + hiveDg = true; + }catch(err){ + console.log(err); + console.log('returning back err'); + hiveDg = false; + } } + return (steemDg || hiveDg); } function getVoteRShares(voteWeight, account, power) { @@ -1043,18 +1224,29 @@ async function steemPowerToVests (steemPower) { return parseFloat(steemPower * totalVests / totalSteem).toFixed(6); } +//function handles conversting SP to Vests +async function hivePowerToVests (hivePower) { + + if (isNaN(totalHive) || isNaN(totalHiveVests) ){ + hiveProps = await hiveClient.database.getDynamicGlobalProperties() + totalHive = Number(hiveProps.total_vesting_fund_steem.split(' ')[0]) + totalHiveVests = Number(hiveProps.total_vesting_shares.split(' ')[0]) + } + return parseFloat(hivePower * totalHiveVests / totalHive).toFixed(6); +} + function sortArrLodash (arrToSort) { return _.orderBy(arrToSort, function (o) { return new Number(o.balance)},['desc']); } function removeArrMatchLodash (arrToClean, arrToMatch, field) { let removedEntries = _.remove(arrToClean, obj => arrToMatch.includes(obj[field])); - console.log("removedEntries"); - console.log(removedEntries); + //console.log("removedEntries"); + //console.log(removedEntries); return arrToClean; } -async function rewardPost(post_url, vp){ +async function rewardPost(post_url, vp, bchain){ //extract author and permalink from full url //check if string ends with /, remove it if (post_url.slice(-1) == '/'){ @@ -1064,6 +1256,7 @@ async function rewardPost(post_url, vp){ let permalink = post_url.split('/').reverse()[0]; //before last portion is author, and remove the starting @ let author = post_url.split('/').reverse()[1].replace('@',''); + await setProperNode(bchain); //cast vote let result = await steem.broadcast.voteAsync( config.rewards_account_pkey, //postingWIF @@ -1095,9 +1288,14 @@ async function verifyGadgetTransaction(userA, gadget_id, tx_type, block_num, tx_ return false; } -async function verifyFriendTransaction(userA, userB, tx_type, block_num, tx_id){ - let trx = await client.database.getTransaction({id: tx_id, block_num: block_num}); +async function verifyFriendTransaction(userA, userB, tx_type, block_num, tx_id, bchain){ + let trx try{ + if (bchain == 'STEEM'){ + trx = await client.database.getTransaction({id: tx_id, block_num: block_num}); + }else if (bchain == 'HIVE'){ + trx = await hiveClient.database.getTransaction({id: tx_id, block_num: block_num}); + } if (trx && trx.operations && trx.operations.length > 0){ console.log(trx.operations[0][1]); @@ -1154,4 +1352,6 @@ async function verifyFriendTransaction(userA, userB, tx_type, block_num, tx_id){ verifyFriendTransaction: verifyFriendTransaction, verifyGadgetTransaction: verifyGadgetTransaction, removeArrMatchLodash: removeArrMatchLodash, + validateAccountLogin: validateAccountLogin, + processSteemTrx: processSteemTrx, } From 38c6141b4353b7af89ebc2f284e5dadacac0d1da Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:40:06 +0300 Subject: [PATCH 144/193] Support Gadgets Rewards Append support for rewards based on gadgets --- curation-bot.js | 498 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 488 insertions(+), 10 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index f7d82a6..ff84b0e 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -28,6 +28,9 @@ var lucky_winner_id = -1; let usersAFITXBal = []; let topUsersAFITX = []; +let gadgetsFetched = false; +let activeGadgets = []; + //version of the reward system var reward_sys_version = 'v0.2'; @@ -164,6 +167,28 @@ const ssc = new SSC(config.steem_engine_rpc); // Top 25 will be stored in topUsersAFITX fetchAFITXBal(0); +//grab list of active gadgets +async function fetchGadgets(){ + try{ + console.log('>>>>>>>>>>>>>fetchGadgets'); + + //let fetch_gadgets_res = await axios.get(config.api_url + 'activeGadgets'); + let gadUrl = config.api_url + 'activeGadgets'; + if (config.testing){ + gadUrl = config.api_test_url + 'activeGadgets'; + } + console.log(gadUrl); + let fetch_gadgets_res = await axios.get(gadUrl); + + activeGadgets = fetch_gadgets_res.data; + gadgetsFetched = true; + console.log(activeGadgets); + }catch(err){ + console.log(err); + } +} + + setInterval(function(){ try{ if (!is_voting){ @@ -244,6 +269,15 @@ MongoClient.connect(url, function(err, client) { // Get the documents collection collection = db.collection(collection_name); + + /*let test = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next(); + console.log(test);*/ + + /*let gadgetId = new ObjectId("5db77cb5e279c25040134953") + let test = await db.collection('user_gadgets').findOne({ benefic: {$in: ['silvertop', '@silvertop']}, status: "active", gadget: gadgetId, }) + console.log(test);*/ + + //testBoostData(); //only start the process once we connected to the DB startProcess(); @@ -267,6 +301,268 @@ if (!config.testing){ } + + +async function testBoostData(){ + await fetchGadgets(); + + let postData = []; + + let boost_res; + + /*let boost_res = await grabConsumeUserBoostByType('@mcfarhat', 'SPORTS', 'unit', {author: 'mcfarhat', permlink: 'bingo'}, true); + console.log('testBoostData 1'); + postData = boost_res.user_post_boosts; + + //check if user has a SPORTS boost as percent increments + let appendNetPercTokens = boost_res.extra_boost; + console.log(appendNetPercTokens); + console.log(postData); + + //let test = await grabConsumeUserBoostByType('@mcfarhat', 'AFIT', 'percent_reward', {author: 'mcfarhat', 'permlink': 'bingo'}, true); + boost_res = await grabConsumeUserBoostByType('@mcfarhat', 'AFIT', 'unit', {author: 'mcfarhat', permlink: 'bingo'}, true); + + postData = postData.concat(boost_res.user_post_boosts); + appendNetPercTokens = boost_res.extra_boost; + console.log('testBoostData 2');*/ + + /* + let boost_res = await grabConsumeUserBoostByType('@mcfarhat', 'AFIT', 'range', {author: 'mcfarhat', permlink: 'bingo'}, true); + + postData = postData.concat(boost_res.user_post_boosts); + + //check if user has a User Rank boost as percent increments + let appendTokens = boost_res.extra_boost; + console.log('>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<'); + //console.log(test); + //store used boosts to the post + //console.log(test.user_post_boosts); + console.log(appendTokens); + console.log(postData); + + console.log(boost_res.user_post_boosts[0].productdetails[0].benefits.boosts); + */ + + + boost_res = await grabConsumeUserBoostByType('@mcfarhat', 'APX', 'percent', {author: 'mcfarhat', permlink: 'bingo'}, true); + + postData = postData.concat(boost_res.user_post_boosts); + + //check if user has a User Rank boost as percent increments + appendTokens = boost_res.extra_boost; + console.log('>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<'); + //console.log(test); + //store used boosts to the post + //console.log(test.user_post_boosts); + console.log(appendTokens); + console.log(postData); + + console.log(boost_res.user_post_boosts[0].productdetails[0].benefits.boosts); + + /*boost_res = await grabConsumeUserBoostByType('@mcfarhat', 'SPORTS', 'percent_reward', {author: 'mcfarhat', permlink: 'bingo'}, true); + + postData = postData.concat(boost_res.user_post_boosts); + + //check if user has a User Rank boost as percent increments + appendTokens = boost_res.extra_boost; + console.log('>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<'); + //console.log(test); + //store used boosts to the post + //console.log(test.user_post_boosts); + console.log(appendTokens); + console.log(postData); + + console.log(boost_res.user_post_boosts[0].productdetails[0].benefits.boosts); + + boost_res = await grabConsumeUserBoostByType('@mcfarhat', 'User Rank', 'unit', {author: 'mcfarhat', permlink: 'bingo'}, true); + + postData = postData.concat(boost_res.user_post_boosts); + + //check if user has a User Rank boost as percent increments + appendTokens = boost_res.extra_boost; + console.log('>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<'); + //console.log(test); + //store used boosts to the post + //console.log(test.user_post_boosts); + console.log(appendTokens); + console.log(postData); + + console.log(boost_res.user_post_boosts[0].productdetails[0].benefits.boosts);*/ + + //check if user has a User Rank boost as percent increments + //console.log(test.extra_boost); +} + +//unit contains values such as SPORTS, AFIT, User Rank +//type contains values such as unit, percent +async function grabConsumeUserBoostByType(user, unit, type, post, consume){ + + //console.log(activeGadgets); + let extra_boost = 0; + user = user.replace('@',''); + let userSteem = '@' + user; + let userNamesVar = [userSteem, user]; + let user_post_boosts = []; + + //apply boosts by user + if (Array.isArray(activeGadgets) && activeGadgets.length > 0){ + let matchingGadgets = activeGadgets.filter( gadget => userNamesVar.includes (gadget.user) ); + let matchingFriendGadgets = activeGadgets.filter( gadget => userNamesVar.includes (gadget.benefic) ); + //console.log(matchingGadgets); + //console.log(matchingFriendGadgets); + if (!Array.isArray(matchingGadgets)){ + matchingGadgets = matchingFriendGadgets; + }else if (Array.isArray(matchingFriendGadgets)){ + matchingGadgets = matchingGadgets.concat(matchingFriendGadgets); + } + console.log('>>>>matchingGadgets'); + console.log(matchingGadgets); + let maxCount = matchingGadgets.length; + //go through each gadget and process its boosts + for (let i=0;i 0){ + let boosts = matchingGadgets[i].productdetails[0].benefits.boosts; + if (Array.isArray( boosts) && boosts.length > 0){ + let maxBoosts = boosts.length; + //to ensure we only consume proper matching boosts + let match = false; + + let is_benefic = false; + for (let j=0;j rwd_post.author == post.author && rwd_post.permlink == post.permlink))){ + skip = true; + } + //check if this is a benefic case and which has multiple boosts, so that we dont consume it more than once. + + if (maxBoosts > 1 && is_benefic){ + try{ + //find its index in the original gadgets array, and make sure it gets only consumed this time + let gad_index = activeGadgets.findIndex(gadget => userNamesVar.includes (gadget.benefic) ); + + if (activeGadgets[gad_index].roundConsumed){ + skipConsumption = true; + } + activeGadgets[gad_index].roundConsumed = true; + }catch(boostErr){ + console.log(boostErr); + } + } + if (!skip){ + if (!Array.isArray(gadget_match.posts_consumed)){ + gadget_match.posts_consumed = []; + } + let consumed_pst = new Object(); + consumed_pst.author = post.author; + consumed_pst.permlink = post.permlink; + //consumed_pst.benefic = post.benefic; + + gadget_match.posts_consumed.push(consumed_pst); + if (!skipConsumption){ + gadget_match.consumed += 1; + if (gadget_match.consumed >= gadget_match.span){ + gadget_match.status="consumed"; + } + } + gadget_match.last_updated = new Date(); + console.log('updating user gadget'); + console.log(gadget_match); + if (!config.testing){ + let transaction = db.collection('user_gadgets').save(gadget_match); + console.log('success inserting post data'); + } + } + } + }catch(err){ + console.log(err); + return; + } + } + } + } + } + } + console.log('user_post_boosts'); + console.log(user_post_boosts); + return {'extra_boost': extra_boost, 'user_post_boosts':user_post_boosts}; +} + + + var votePosts; var lastIterationCount = 0; @@ -404,6 +700,10 @@ async function startProcess() { claimRewards(); } + //fetch gadget assignments + let fet_res = await fetchGadgets(); + + //console.log(activeGadgets); //reset number of helping votes case helping_accounts_votes = 0; @@ -457,6 +757,11 @@ async function startProcess() { console.log('skippable_posts'); console.log(skippable_posts); + //grab list of active gadgets + //fetchGadgets(); + + + var query = {tag: config.main_tag, limit: 100}; if (config.testing){ @@ -894,6 +1199,8 @@ function processVotes(query, subsequent) { permlink: post.permlink }).upsert().replaceOne(post_entry_skip); + proceed_bulk_posts_skip = true; + continue; } @@ -909,7 +1216,7 @@ function processVotes(query, subsequent) { //console.log('activity score:'+post.activity_score); //skip post if it has less than min activity recorded - if (post.activity_score == 0){ + if (!config.testing && post.activity_score == 0){ continue; } @@ -1015,6 +1322,8 @@ function processVotes(query, subsequent) { url: comment_transaction.url, comment_url: comment_transaction.comment_url }).upsert().replaceOne(comment_transaction); + + proceed_bulk_transactions = true; utils.log('found comment>>>>'); utils.log(comment_transaction); } @@ -1030,6 +1339,34 @@ function processVotes(query, subsequent) { var user_rank_info = await axios.get(rank_api_url); //utils.log(user_rank_info.user_rank); post.user_rank = user_rank_info.data.user_rank; + + console.log('old user rank:'+post.user_rank); + + let boost_res = await grabConsumeUserBoostByType(post.author, 'User Rank', 'percent', post, true); + //store used boosts to the post + post.user_post_boosts = boost_res.user_post_boosts; + + //check if user has a User Rank boost as percent increments + let appendPercRank = boost_res.extra_boost; + + //append as percentage + post.user_rank += appendPercRank * post.user_rank / 100; + post.boost_user_rank_percent = appendPercRank; + + console.log('new user rank:'+post.user_rank); + + boost_res = await grabConsumeUserBoostByType(post.author, 'User Rank', 'unit', post, true); + post.user_post_boosts = post.user_post_boosts.concat(boost_res.user_post_boosts); + + //check if user has a User Rank boost as percent increments + let appendUnitRank = boost_res.extra_boost; + + //append as percentage + post.user_rank += appendUnitRank; + post.boost_user_rank_unit = appendUnitRank; + + console.log('new user rank:'+post.user_rank); + //calculate user rank score relying on positive votes only post.user_rank_score = parseFloat(user_rank_info.data.user_rank)*parseInt(config.rank_factor)/100; //utils.log('rank'+post.user_rank_score); @@ -1038,6 +1375,55 @@ function processVotes(query, subsequent) { //calculate total post score post.post_score = post.activity_score + post.content_score + post.media_score + post.upvote_score + post.comment_score + post.moderator_score + post.user_rank_score; + console.log('old post score:'+post.post_score); + + post.afit_pre_boost = post.post_score; + + //check if user has an AFIT boost as percent increments + boost_res = await grabConsumeUserBoostByType(post.author, 'AFIT', 'percent_reward', post, true); + + post.user_post_boosts = post.user_post_boosts.concat(boost_res.user_post_boosts); + + //check if user has a User Rank boost as percent increments + let appendPercTokens = boost_res.extra_boost; + + //append as percentage + post.post_score += appendPercTokens * post.post_score / 100; + post.boost_afit_percent_reward = appendPercTokens; + + console.log('new post score:'+post.post_score); + + //check if user has an AFIT boost as unit entries + + boost_res = await grabConsumeUserBoostByType(post.author, 'AFIT', 'unit', post, true); + + post.user_post_boosts = post.user_post_boosts.concat(boost_res.user_post_boosts); + + //check if user has a User Rank boost as percent increments + let appendTokens = boost_res.extra_boost; + + //append tokens + post.post_score += appendTokens; + post.boost_afit_units = appendTokens; + + console.log('new post score:'+post.post_score); + + //check if user has an AFIT boost as range entries + + boost_res = await grabConsumeUserBoostByType(post.author, 'AFIT', 'range', post, true); + + post.user_post_boosts = post.user_post_boosts.concat(boost_res.user_post_boosts); + + //check if user has a User Rank boost as percent increments + appendTokens = boost_res.extra_boost; + + //append tokens + post.post_score += appendTokens; + post.boost_afit_units = appendTokens; + + console.log('new post score:'+post.post_score); + + //rate multiplier to allow assigning proper steem upvote value per each post according to its post_score/afit payout post.rate_multiplier = post.post_score / 100; //post_scores.push([post.url,post.post_score]); @@ -1058,7 +1444,7 @@ function processVotes(query, subsequent) { bulk.find( { permlink: post.permlink } ).upsert().replaceOne( post ); - + proceed_bulk = true; //post token rewards DB transaction //by default the reward owner is the author @@ -1097,6 +1483,7 @@ function processVotes(query, subsequent) { reward_activity: post_transaction.reward_activity, url: post_transaction.url }).upsert().replaceOne(post_transaction); + proceed_bulk_transactions = true; //the proper transaction without reward if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ @@ -1119,7 +1506,7 @@ function processVotes(query, subsequent) { reward_activity: charity_trans.reward_activity, url: charity_trans.url }).upsert().replaceOne(charity_trans); - + proceed_bulk_transactions = true; } //reward upvoters @@ -1173,7 +1560,7 @@ function processVotes(query, subsequent) { url: vote_transaction.url }).upsert().replaceOne(vote_transaction); //transactions.push(vote_transaction); - + proceed_bulk_transactions = true; //utils.log(vote_transaction); } }); @@ -1185,28 +1572,37 @@ function processVotes(query, subsequent) { }//end of loop going through posts //properly stored any future skippable posts + utils.log(proceed_bulk_posts_skip); try{ + if (proceed_bulk_posts_skip){ await bulk_posts_skip.execute(); + } }catch(bulkerr){ utils.log(bulkerr); } + utils.log('votePosts.length:'+votePosts.length); + utils.log(proceed_bulk); + utils.log(proceed_bulk_transactions); if (votePosts.length>0){ try{ //store posts + if (proceed_bulk){ await bulk.execute(); + } }catch(bulkerr){ utils.log(bulkerr); } try{ //award transaction tokens + if (proceed_bulk_transactions){ await bulk_transactions.execute(); + } }catch(bulkerr){ utils.log(bulkerr); } } - //if this is the first try, or the new count of posts is bigger than the one before, let's try adding again if (!config.testing && (!subsequent || votePosts.length > lastIterationCount || queryCount < config.max_query_count)){ @@ -1309,10 +1705,10 @@ function processVotes(query, subsequent) { //get vote value at 100% let full_vote_value = getVoteValueUSD(100, account, 100, sbd_price); console.log('full_vote_value') - console.log(full_vote_value) + //console.log(full_vote_value) console.log('topUsersAFITX') - console.log(topUsersAFITX); + //console.log(topUsersAFITX); //number of found exchanges to perform in coming round let matched_exchanges = 0; @@ -1518,7 +1914,7 @@ async function fetchAFITXBal(offset){ //skip first entry as thats Actifit account topUsersAFITX = usersAFITXBal.slice(1, config.topAFITXCount); - console.log(topUsersAFITX); + //console.log(topUsersAFITX); } } }catch(err){ @@ -1553,7 +1949,9 @@ function votingProcess(posts, power_per_vote) { saveState(); //since we're done voting, we need to update all user tokens to reflect new rewards + if (!config.testing){ updateUserTokens(); + } //reportEmail(config.report_emails) }, config.voting_posting_delay); } @@ -1564,10 +1962,14 @@ function votingProcess(posts, power_per_vote) { } -function sendVote(post, retries, power_per_vote) { +async function sendVote(post, retries, power_per_vote) { + + utils.log('Voting on: ' + post.url + ' with count'+post.json.step_count); var token_count = post.post_score;//parseFloat(post.rate_multiplier)*100; + console.log(power_per_vote); + var vote_weight = Math.floor(post.rate_multiplier * power_per_vote); let stdrd_vote_weight = vote_weight * config.partner_comm_vote_mult; console.log('vote weight:'+vote_weight); @@ -1594,10 +1996,86 @@ function sendVote(post, retries, power_per_vote) { stdrd_vote_weight = config.max_vote_per_post; } + let net_rewards_vote_weight = stdrd_vote_weight; + + console.log('old sports percent:'+stdrd_vote_weight); + + /*if (config.testing){ + console.log('switch author'); + console.log(post.author); + post.author = 'mcfarhat'; + post.permlink = 'actifit-witness-vote-application-msp'; + }*/ + + let boost_res = await grabConsumeUserBoostByType(post.author, 'SPORTS', 'percent_reward', post, true); + + post.user_post_boosts = post.user_post_boosts.concat(boost_res.user_post_boosts); + + //check if user has a SPORTS boost as percent increments + let appendPercTokens = boost_res.extra_boost; + + //append as percentage + stdrd_vote_weight += appendPercTokens * stdrd_vote_weight / 100; + stdrd_vote_weight = Math.floor(stdrd_vote_weight); + post.boost_sports_percent_reward = appendPercTokens; + + console.log('new sports percent:'+stdrd_vote_weight); + + //check if user has a SPORTS boost as percent increments + + boost_res = await grabConsumeUserBoostByType(post.author, 'SPORTS', 'percent', post, true); + + post.user_post_boosts = post.user_post_boosts.concat(boost_res.user_post_boosts); + + //check if user has a SPORTS boost as percent increments + let appendNetPercTokens = boost_res.extra_boost; + + //append as percentage + stdrd_vote_weight += appendNetPercTokens * 100; + stdrd_vote_weight = Math.floor(stdrd_vote_weight); + post.boost_sports_percent = appendNetPercTokens; + + console.log('new sports percent:'+stdrd_vote_weight); + + + //check if user has an APPICS boost as percent + + //first need to make sure the post benefits from APX vote (meaning has APX tag) + + let tags = JSON.parse(post.json_metadata).tags; + + console.log('checking APX '); + //console.log(tags); + //console.log(tags.findIndex(item => config.appics_tag.toLowerCase() === item.toLowerCase())); + + if(tags && tags.length > 0 && tags.findIndex(item => config.appics_tag.toLowerCase() === item.toLowerCase()) != -1) { + + utils.log('Post contains APX tag for extra vote ' + post.url); + + let curAuthor = post.author; + if (config.testing){ + curAuthor= 'mcfarhat'; + } + boost_res = await grabConsumeUserBoostByType(curAuthor, 'APX', 'percent', post, true); + + post.user_post_boosts = post.user_post_boosts.concat(boost_res.user_post_boosts); + + let appendApxPercTokens = boost_res.extra_boost; + + //append as percentage + let boost_apx_percent = appendApxPercTokens * 100; + boost_apx_percent = Math.floor(boost_apx_percent); + post.boost_apx_percent = boost_apx_percent; + post.vote_appics = true; + + console.log('new APX percent:' + post.boost_apx_percent); + + } + post.vote_weight = vote_weight; last_votes.push(post); - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { if(config.testing){ //resolve(''); From 15f79c0d864b6519d3135d1521e725fc89705c69 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:45:45 +0300 Subject: [PATCH 145/193] Voting Round Multi-Chain Support Append support for multi-chain rewards, with Hive being master voting blockchain whereby initial actifit report posts will be fetched, and steem as legacy blockchain with rewarding posts posted there as well. --- curation-bot.js | 765 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 728 insertions(+), 37 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index ff84b0e..76066e0 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -5,6 +5,7 @@ var mail = require('./mail'); var _ = require('lodash'); var moment = require('moment'); const MongoClient = require('mongodb').MongoClient; +let ObjectId = require('mongodb').ObjectId; const cheerio = require('cheerio') const axios = require('axios'); @@ -39,6 +40,16 @@ var error_sent = false; var steem_price = 1; // This will get overridden with actual prices if a price_feed_url is specified in settings var sbd_price = 1; // This will get overridden with actual prices if a price_feed_url is specified in settings +let hive_price = 1; +let hbd_price = 1; + +// Load the settings from the config file +loadConfig(); + +loadHivePrices(); +//kick off loading steem prices in 30 seconds +setTimeout(loadSteemPrices, 30*1000); + var STEEMIT_100_PERCENT = 10000; var STEEMIT_VOTE_REGENERATION_SECONDS = (5 * 60 * 60 * 24); var HOURS = 60 * 60; @@ -65,6 +76,12 @@ setInterval(function() { //console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received //console.log('error: '+ error) }); + + //also load prices & broadcast updates to witness nodes (STEEM & HIVE) + loadHivePrices(); + //in 30 seconds load steem prices + setTimeout(loadSteemPrices, 30*1000); + } }catch(err){ @@ -73,7 +90,8 @@ setInterval(function() { }, 600000); // every 10 minutes (600000) -var crypto = require('crypto'); + +let crypto = require('crypto'); const activity_rules = [ [4999,0], @@ -282,8 +300,12 @@ MongoClient.connect(url, function(err, client) { startProcess(); // Load updated STEEM and SBD prices every 30 minutes - loadPrices(); - setInterval(loadPrices, 30 * 60 * 1000); + /*loadPrices(); + setInterval( function (){ + if (!is_voting){ + loadPrices() + } + }, 30 * 60 * 1000);*/ //updateUserTokens(); } else { @@ -791,7 +813,50 @@ async function startProcess() { } -function loadPrices() { +function setSteemPrice(json){ + steem_price = parseFloat(json.steem.usd); + console.log('STEEM price:'+steem_price) + broadcastFeed('STEEM') +} + +function setSbdPrice(json){ + sbd_price = parseFloat(json['steem-dollars'].usd); + console.log('SBD price:'+sbd_price) +} + +function setHivePrice(json){ + hive_price = parseFloat(json.hive.usd); + console.log('HIVE price:'+hive_price) + broadcastFeed('HIVE') +} + +function setHbdPrice(json){ + hbd_price = parseFloat(json['hive_dollar'].usd); + console.log('HBD price:'+hbd_price) +} + +function loadHivePrices() { + fetch('https://api.coingecko.com/api/v3/simple/price?ids=hive&vs_currencies=usd').then( + res => {res.json().then(json => setHivePrice(json)).catch(e => console.log('Error loading STEEM price: ' + e)) + }).catch(e => console.log('Error loading HIVE price: ' + e)) + + //grab SBD price + fetch('https://api.coingecko.com/api/v3/simple/price?ids=hive_dollar&vs_currencies=usd').then( + res => {res.json().then(json => setHbdPrice(json)).catch(e => console.log('Error loading SBD price: ' + e)) + }).catch(e => console.log('Error loading HBD price: ' + e)) +} + +function loadSteemPrices() { + fetch('https://api.coingecko.com/api/v3/simple/price?ids=steem&vs_currencies=usd').then( + res => {res.json().then(json => setSteemPrice(json)).catch(e => console.log('Error loading STEEM price: ' + e)) + }).catch(e => console.log('Error loading STEEM price: ' + e)) + + //grab SBD price + fetch('https://api.coingecko.com/api/v3/simple/price?ids=steem-dollars&vs_currencies=usd').then( + res => {res.json().then(json => setSbdPrice(json)).catch(e => console.log('Error loading SBD price: ' + e)) + }).catch(e => console.log('Error loading SBD price: ' + e)) + + /* if(config.price_source == 'coinmarketcap') { // Load the price feed data request.get('https://api.coinmarketcap.com/v1/ticker/steem/', function (e, r, data) { @@ -861,6 +926,11 @@ function processVotes(query, subsequent) { //fetchAFITXBal(0); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true +}); + steem.api.getDiscussionsByCreated(query, async function (err, result) { //track how many queries were ran queryCount += 1; @@ -879,6 +949,9 @@ function processVotes(query, subsequent) { var bulk_posts_skip = db.collection('posts_to_skip').initializeUnorderedBulkOp(); + let proceed_bulk = false; + let proceed_bulk_transactions = false; + let proceed_bulk_posts_skip = false; for(var i = 0; i < result.length; i++) { @@ -1091,7 +1164,7 @@ function processVotes(query, subsequent) { //we've got a problem, skip this post/guy. We might want to report too. continue; } - } + //check if the post has an encryption key val, and ensure it is the proper one @@ -1112,6 +1185,8 @@ function processVotes(query, subsequent) { continue; } + } + //console.log('still here'); //moving this section before the actual token rewards @@ -1338,7 +1413,7 @@ function processVotes(query, subsequent) { var rank_api_url = config.api_url+'getRank/'+post.author; var user_rank_info = await axios.get(rank_api_url); //utils.log(user_rank_info.user_rank); - post.user_rank = user_rank_info.data.user_rank; + post.user_rank = parseFloat(user_rank_info.data.user_rank); console.log('old user rank:'+post.user_rank); @@ -2105,61 +2180,242 @@ async function sendVote(post, retries, power_per_vote) { resolve(''); } }else{ + + let res; + + + if (config.hive_voting_active){ + + //first bchain transactions + console.log('set HIVE node'); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); //vote first using pay and funds accounts only if we have an AFIT/STEEM exchange operation and we have room to upvote using helping accounts if (post.additional_vote_weight && post.helperVotes){ let vote_percent_add_accounts = config.helping_account_percent;//at 50%: 5000 try{ - steem.broadcast.vote(config.full_pay_posting_key, config.full_pay_benef_account, post.author, post.permlink, vote_percent_add_accounts, function (err, result) { + utils.log('voting with '+config.full_pay_benef_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); - if (!err && result) { - utils.log(err, result); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.full_pay_benef_account, + "author": post.author, + "permlink": post.permlink, + "weight": vote_percent_add_accounts + } + ] + ], + extensions: [] + }, + { posting: config.full_pay_posting_key } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); } - }); + }catch(err){ utils.log(err); } + try{ - steem.broadcast.vote(config.pay_account_post_key, config.pay_account, post.author, post.permlink, vote_percent_add_accounts, function (err, result) { utils.log('voting with '+config.pay_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); - if (!err && result) { - utils.log(err, result); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.pay_account, + "author": post.author, + "permlink": post.permlink, + "weight": vote_percent_add_accounts + } + ] + ], + extensions: [] + }, + { posting: config.pay_account_post_key } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); } - }); + }catch(err){ utils.log(err); } + } //if additional partner accounts enabled, vote using them as well try{ if (config.zzan_active){ - steem.broadcast.vote(config.zzan_pk, config.zzan_account, post.author, post.permlink, stdrd_vote_weight, function (err, result) { utils.log('voting with '+config.zzan_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); - if (!err && result) { - utils.log(err, result); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.zzan_account, + "author": post.author, + "permlink": post.permlink, + "weight": stdrd_vote_weight + } + ] + ], + extensions: [] + }, + { posting: config.zzan_pk } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); } + + } + }catch(err){ + utils.log(err); + } + + try{ + if (config.sports_active){ + utils.log('voting with '+config.sports_active+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.sports_account, + "author": post.author, + "permlink": post.permlink, + "weight": stdrd_vote_weight + } + ] + ], + extensions: [] + }, + { posting: config.sports_pk } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + } }catch(err){ utils.log(err); } + //append appics account gadget-based voting + + if (config.appics_active && post.vote_appics){ + try{ - if (config.sports_active){ - steem.broadcast.vote(config.sports_pk, config.sports_account, post.author, post.permlink, stdrd_vote_weight, function (err, result) { - utils.log('voting with '+config.sports_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); - if (!err && result) { - utils.log(err, result); + utils.log('voting with '+config.appics_account+ ' '+utils.format(post.boost_apx_percent / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.appics_account, + "author": post.author, + "permlink": post.permlink, + "weight": post.boost_apx_percent + } + ] + ], + extensions: [] + }, + { posting: config.appics_pk } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + + }catch(err){ + utils.log(err); } + } + + try{ + utils.log('voting with '+config.rewards_account+ ' '+utils.format(net_rewards_vote_weight / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.rewards_account, + "author": post.author, + "permlink": post.permlink, + "weight": net_rewards_vote_weight } + ] + ], + extensions: [] + }, + { posting: config.rewards_account_pk } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + }catch(err){ utils.log(err); } - steem.broadcast.vote(config.posting_key, account.name, post.author, post.permlink, vote_weight, function (err, result) { - if (!err && result) { - utils.log(utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); + try{ + utils.log('voting with '+account.name+ ' '+utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": account.name, + "author": post.author, + "permlink": post.permlink, + "weight": vote_weight + } + ] + ], + extensions: [] + }, + { posting: config.posting_key } + ).then(async function(rest, err) { //store exchange transaction as complete if (post.additional_vote_weight){ @@ -2172,9 +2428,50 @@ async function sendVote(post, retries, power_per_vote) { db.collection('exchange_afit_steem').save(cur_upvote_entry); } + if(config.comment_location && config.comment){ + await sendComment(post, 0, vote_weight, config.active_hive_node) + .then( res => { + //resolve(res) + + + console.log(res) + }).catch(err => { + console.log(err); + }) + + } + }).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + + + + + + //wait 5 seconds before commenting + //await delay(5000); + + //setTimeout(function () { + /* await sendComment(post, 0, vote_weight, config.active_hive_node) + .then( res => { + //resolve(res) + console.log(res) + }) + .catch(err => { + console.log(err); + })*/ + //}, config.voting_posting_delay); + /*}else{ + //resolve(result); + }*/ + }else{ + // Try again one time on error + /*if (retries < config.max_vote_comment_retries){ + //try to vote again setTimeout(function () { - sendComment(post, 0, vote_weight) + sendVote(post, retries + 1, power_per_vote) .then( res => { resolve(res) }) @@ -2183,13 +2480,308 @@ async function sendVote(post, retries, power_per_vote) { }) }, config.voting_posting_delay); }else{ - resolve(result); + var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' + utils.log(message); + reject(err); + //errorEmail(message, config.report_emails); + }*/ + } + + + + }catch(mainerr){ + utils.log(mainerr); + } + } + + if (config.steem_voting_active){ + + /*************************************************/ + /*************************************************/ + /*************************************************/ + + //second blockchain transactions + console.log('set STEEM node'); + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + //vote first using pay and funds accounts only if we have an AFIT/STEEM exchange operation and we have room to upvote using helping accounts + if (post.additional_vote_weight && post.helperVotes){ + let vote_percent_add_accounts = config.helping_account_percent;//at 50%: 5000 + try{ + + utils.log('voting with '+config.full_pay_benef_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); + + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.full_pay_benef_account, + "author": post.author, + "permlink": post.permlink, + "weight": vote_percent_add_accounts + } + ] + ], + extensions: [] + }, + { posting: config.full_pay_posting_key } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + + }catch(err){ + utils.log(err); + } + + try{ + utils.log('voting with '+config.pay_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.pay_account, + "author": post.author, + "permlink": post.permlink, + "weight": vote_percent_add_accounts + } + ] + ], + extensions: [] + }, + { posting: config.pay_account_post_key } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + + }catch(err){ + utils.log(err); + } + + } + + //if additional partner accounts enabled, vote using them as well + try{ + if (config.zzan_active){ + utils.log('voting with '+config.zzan_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.zzan_account, + "author": post.author, + "permlink": post.permlink, + "weight": stdrd_vote_weight + } + ] + ], + extensions: [] + }, + { posting: config.zzan_pk } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + + } + }catch(err){ + utils.log(err); + } + + try{ + if (config.sports_active){ + utils.log('voting with '+config.sports_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.sports_account, + "author": post.author, + "permlink": post.permlink, + "weight": stdrd_vote_weight + } + ] + ], + extensions: [] + }, + { posting: config.sports_pk } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + } + }catch(err){ + utils.log(err); + } + + //append appics account gadget-based voting + + if (config.appics_active && post.vote_appics){ + + try{ + utils.log('voting with '+config.appics_account+ ' '+utils.format(post.boost_apx_percent / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.appics_account, + "author": post.author, + "permlink": post.permlink, + "weight": post.boost_apx_percent + } + ] + ], + extensions: [] + }, + { posting: config.appics_pk } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + + }catch(err){ + utils.log(err); + } + } + + try{ + utils.log('voting with '+config.rewards_account+ ' '+utils.format(net_rewards_vote_weight / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": config.rewards_account, + "author": post.author, + "permlink": post.permlink, + "weight": net_rewards_vote_weight + } + ] + ], + extensions: [] + }, + { posting: config.rewards_account_pk } + ).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + } + + }catch(err){ + utils.log(err); + } + + try{ + + utils.log('voting with '+account.name+ ' '+utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + res = await steem.broadcast.sendAsync( + { + operations: [ + ['vote', + { + "voter": account.name, + "author": post.author, + "permlink": post.permlink, + "weight": vote_weight + } + ] + ], + extensions: [] + }, + { posting: config.posting_key } + ) + .then(async function(rest, err) { + + //store exchange transaction as complete + if (post.additional_vote_weight){ + console.log('Exchange vote'); + let cur_upvote_entry = afit_steem_upvote_list.find( entry => entry.post_author === post.author && + entry.post_permlink === post.permlink && + entry.user === post.author); + console.log(cur_upvote_entry); + cur_upvote_entry.upvote_processed = true; + db.collection('exchange_afit_steem').save(cur_upvote_entry); + } + + if(config.comment_location && config.comment){ + + //setTimeout(function () { + + //wait 5 seconds before commenting + //await delay(5000); + + await sendComment(post, 0, vote_weight, config.active_node) + .then( res => { + //resolve(res) + }).catch(err => { + //reject(err); + }) + //}, config.voting_posting_delay); }else{ - utils.log(err, result); + //resolve(''); + } + }).catch(e => console.log(e)) + console.log(res); + if (res && res.block_num) { + utils.log('success'); + + //store exchange transaction as complete + /*if (post.additional_vote_weight){ + console.log('Exchange vote'); + let cur_upvote_entry = afit_steem_upvote_list.find( entry => entry.post_author === post.author && + entry.post_permlink === post.permlink && + entry.user === post.author); + console.log(cur_upvote_entry); + cur_upvote_entry.upvote_processed = true; + db.collection('exchange_afit_steem').save(cur_upvote_entry); + }*/ + + }else{ // Try again one time on error - if (retries < config.max_vote_comment_retries){ + /*if (retries < config.max_vote_comment_retries){ //try to vote again setTimeout(function () { sendVote(post, retries + 1, power_per_vote) @@ -2205,9 +2797,15 @@ async function sendVote(post, retries, power_per_vote) { utils.log(message); reject(err); //errorEmail(message, config.report_emails); + }*/ + } + + }catch(mainerr){ + utils.log(mainerr); } + } - }); + resolve(''); } }); } @@ -2248,7 +2846,7 @@ async function updateUserTokens() { } -function sendComment(post, retries, vote_weight) { +async function sendComment(post, retries, vote_weight, bchain_node) { var parentAuthor = post.author; var parentPermlink = post.permlink; var rate_multiplier = post.rate_multiplier; @@ -2256,7 +2854,7 @@ function sendComment(post, retries, vote_weight) { var content = null; // Return promise - return new Promise((resolve, reject) => { + //return new Promise( async (resolve, reject) => { content = fs.readFileSync(config.comment_location, "utf8"); // If promotion content is specified in the config then use it to comment on the upvoted post @@ -2272,9 +2870,60 @@ function sendComment(post, retries, vote_weight) { token_count = config.sponsored_athlete_afit_reward; } + token_count = Math.ceil(token_count * 10000) / 10000; + // Replace variables in the promotion content content = content.replace(/\{weight\}/g, utils.format(vote_weight / 100)).replace(/\{token_count\}/g,token_count).replace(/\{step_count\}/g,post_step_count); + console.log('post.user_post_boosts'); + console.log(post.user_post_boosts); + + //create proper display for boosts consumed + let boost_content = ''; + let maxGadgets = post.user_post_boosts.length; + boost_content += '
'; + for (let i=0;i < maxGadgets;i++){ + let cur_boost = post.user_post_boosts[i]; + let prod_info = cur_boost.productdetails[0]; + boost_content += '
'; + boost_content += '
'; + //append standard images to display on the other front-ends + boost_content += ''; + + boost_content += '
'; + for (let iter=0;iter < cur_boost.gadget_level;iter++){ + boost_content += ''; + } + boost_content += '
'; + boost_content += '
'+cur_boost.gadget_name + ' - L'+cur_boost.gadget_level+'
'; + console.log('prod_info'); + console.log(prod_info); + let boosts = prod_info.benefits.boosts; + if (Array.isArray( boosts) && boosts.length > 0){ + let maxBoosts = boosts.length; + for (let j=0;j'; + if (cur_boost.benefic && (cur_boost.benefic == post.author || cur_boost.benefic == '@' + post.author)){ + boost_content += '
Thanks to your friend @'+ cur_boost.user + '
'; + } + } + } + boost_content += '
'; + } + boost_content += '
'; + let reward_diff = parseFloat(token_count) - parseFloat(post.afit_pre_boost); + if (reward_diff > 0){ + boost_content += '
Boosts increased your AFIT earnings by '+reward_diff.toFixed(4)+' AFIT
'; + } + content = content.replace(/\{boost_list\}/g, boost_content); + //replace(/\{milestone\}/g, milestone_txt). //adding proper meta content for later relevant reward via afit_tokens data @@ -2305,7 +2954,44 @@ function sendComment(post, retries, vote_weight) { if (!config.testing){ // Broadcast the comment - steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { + + try{ + + steem.api.setOptions({ + url: bchain_node , + //useAppbaseApi: true + }); + console.log(jsonMetadata); + const operations = [ + ['comment', + { + "parent_author": parentAuthor, + "parent_permlink": parentPermlink, + "author": account.name, + "permlink": permlink, + "title": '', + "body": content, + "json_metadata" : JSON.stringify(jsonMetadata) + } + ] + ]; + + console.log(operations); + + let res = await steem.broadcast.sendAsync( + { + operations: operations, + extensions: [] + }, + { posting: config.posting_key } + ).catch(e => console.log(e)) + utils.log(res); + if (res && res.block_num) { + utils.log('Posted comment: ' + permlink); + //resolve(res); + } + + /*steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { if (!err && result) { utils.log('Posted comment: ' + permlink); resolve(result); @@ -2325,17 +3011,22 @@ function sendComment(post, retries, vote_weight) { } //reject(err); } - }); + });*/ + + }catch(com_err){ + utils.log(com_err); + } + }else{ utils.log('comment'); console.log(content); console.log(jsonMetadata); - resolve(''); + //resolve(''); } }else{ - reject('Failed to load content'); + //reject('Failed to load content'); } - }); + //}); // Check if the bot should resteem this post /* if (config.resteem) resteem(parentAuthor, parentPermlink); */ From 8021ab04ffb14b0bfd3c4f2eee96f2fafa3a6066 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:46:39 +0300 Subject: [PATCH 146/193] Append Support Buying & Burning Tokens Append Support Buying & Burning AFIT Tokens on Steem-Engine --- curation-bot.js | 115 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 76066e0..978d4b6 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -172,11 +172,12 @@ const actifit_img_urls = [ "https://cdn.steemitimages.com/DQmeBn1PLf6a3QaXjM23EbQcaKtfDckgtGPHE4DApoUeBEJ/A-1.png", "https://cdn.steemitimages.com/DQmV7NRosGCmNLsyHGzmh4Vr1pQJuBPEy2rk3WvnEUDxDFA/A-21.png", "https://cdn.steemitimages.com/DQmdNAWWwv6MAJjiNUWRahmAqbFBPxrX8WLQvoKyVHHqih1/A-19.png","https://cdn.steemitimages.com/DQmVNqM8wQj2TnfwqSPYtfAuPHYjeBXSFekCHGZw9K3B9Gi/A-16.png", "https://cdn.steemitimages.com/DQma7nn1yV2w9iY6qXDBJUoTWkELTYxot7R9eoG1M3Tbtqn/A-7.png"]; -// Load the settings from the config file -loadConfig(); -var botNames; + +var botNames; +//fetchGadgets(); +let tokensBurntLastRound = false; const SSC = require('sscjs'); const ssc = new SSC(config.steem_engine_rpc); @@ -220,7 +221,7 @@ setInterval(function(){ }, 1200000); // every 20 minutes (1200000) steem.api.setOptions({ - url: config.active_node , + url: config.active_hive_node , //useAppbaseApi: true }); @@ -670,6 +671,12 @@ async function startProcess() { // Load the settings from the config file each time so we can pick up any changes loadConfig(); + + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); + // Load the bot account info steem.api.getAccounts([config.account], function (err, result) { if (err || !result) @@ -707,12 +714,59 @@ async function startProcess() { console.log('is_voting:'+is_voting); console.log('passedOneDay:'+passedOneDay); + + + //BuyAndBurn(true); + + + if (account && !skip && !is_voting && passedOneDay) { // Load the current voting power of the account var vp = utils.getVotingPower(account); + let vpRestart = parseFloat(config.vp_kickstart) - 125; + let vpRestartLimit = vpRestart + 2; + console.log('vpRestart:'+vpRestart); utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp*100))); + + //disabling buying and burning tokens + /*console.log(config.vpTokenBurn); + if (vp >= config.vpTokenBurn/100 && vp < (config.vpTokenBurn+10)/100){ + console.log('Buy AFIT & Burn em Case'); + if (!tokensBurntLastRound){ + BuyAndBurn(false); + tokensBurntLastRound = true; + } + }else{ + tokensBurntLastRound = false; + }*/ + + + if (vp >= vpRestart/100 && vp < vpRestartLimit/100) { + //restart server to avoid voting round breakdown + //https://devcenter.heroku.com/articles/platform-api-reference + console.log('contacting heroku server'); + + request.post( + { + url: 'https://api.heroku.com/apps/' + config.heroku_app_id + '/dynos/' + config.heroku_app_dyno + '/actions/stop', + //url: 'https://api.heroku.com/apps/' + config.heroku_app_id + '/dynos', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/vnd.heroku+json; version=3', + 'Authorization': 'Bearer ' + config.heroku_app_token + } + }, + function(error, response, body) { + // Do stuff + console.log(response); + console.log(body); + } + ); + ///////////////// + } + // We are at voting power kick start - time to vote! //utils.log(vp >= parseFloat(config.vp_kickstart)/100); if (vp >= parseFloat(config.vp_kickstart)/100 || config.testing) { @@ -812,6 +866,59 @@ async function startProcess() { else utils.log('Voting... or waiting for a day to pass'); } +function BuyAndBurn(test){ + console.log('init'); + //fetch sell book + ssc.find('market', 'sellBook', {symbol: 'AFIT'}, 10, 0, [{ index: 'priceDec', descending: false }], (err, result) => { + console.log(result); + //fetch enough sell orders to place our own buy order + let totalSteemSold = 0; + const target = config.targetTokenBurnSteem; + let targetPrice = 0; + for (let i=0, max=result.length;i= target){ + targetPrice = result[i].price; + break; + } + } + console.log('targetPrice:'+targetPrice); + //place order for buying tokens + //calculate needed AFIT quantity to match target + let quantity = Math.ceil(target / targetPrice); + console.log('AFIT to buy:'+quantity); + if (test){ + quantity = 0.1; + } + let json = "{\"contractName\":\"market\",\"contractAction\":\"buy\",\"contractPayload\":{\"symbol\":\"AFIT\",\"quantity\":\"" + quantity + "\",\"price\":\"" + targetPrice + "\"}}"; + + steem.broadcast.customJson(config.active_key, [config.account], [], 'ssc-mainnet1', json, (err, result) => { + if (!err && result) { + console.log('success buyin'); + console.log(result); + + json = "{\"contractName\":\"tokens\",\"contractAction\":\"transfer\",\"contractPayload\":{\"symbol\":\"AFIT\",\"to\":\"null\",\"quantity\":\"" + quantity + "\",\"memo\":\"\"}}"; + //burn those tokens + steem.broadcast.customJson(config.active_key, [config.account], [], 'ssc-mainnet1', json, (err, result) => { + if (!err && result) { + console.log('success burnin'); + console.log(result); + } else { + console.log('err'); + console.log(err); + } + }); + + } else { + console.log('err'); + console.log(err); + } + }); + + + }); +} function setSteemPrice(json){ steem_price = parseFloat(json.steem.usd); From 584a4500be671850d10e18f122121ca2b8beb80d Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:47:31 +0300 Subject: [PATCH 147/193] Append Support for Witness Price Feed Bcast Append Support for Witness Price Feed Broadcast on both Hive & Steem Actifit witness nodes --- curation-bot.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/curation-bot.js b/curation-bot.js index 978d4b6..c106880 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1020,6 +1020,49 @@ function loadSteemPrices() { }); }); } + + */ +} + + +//handles sending out price feed for witness nodes +function broadcastFeed (type) { + //STEEM witness + let peg_multi = config.peg_multi ? config.peg_multi : 1; + let price_val = steem_price; + let pegged_cur = ' SBD'; + let origType = type; + if (type == 'HIVE'){ + price_val = hive_price; + //below two lines are hacks since steem-js is not yet accepting HIVE and HBD + //pegged_cur = ' HBD'; + type = 'STEEM'; + steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); + }else{ + steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + } + let exchange_rate = { base: price_val.toFixed(3) + pegged_cur, quote: (1 / peg_multi).toFixed(3) + ' ' + type }; + utils.log('Broadcasting ' + origType + ' feed_publish transaction: ' + JSON.stringify(exchange_rate)); + steem.broadcast.feedPublish(config.active_key, config.account, exchange_rate, function (err, result) { + if (result && !err) { + console.log(result); + utils.log('Broadcast successful!'); + } else { + utils.log('Error broadcasting feed_publish transaction: ' + err); + + /*if (retries == 5) + failover(); + + if (retries < 2) + setTimeout(function () { publishFeed(price, retries + 1); }, 10 * 1000);*/ + } + }); } From c4f943ec02af0e8c2497f56e34359778252183d3 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 5 Apr 2020 00:48:23 +0300 Subject: [PATCH 148/193] Adjust Reward Value Calculation Adjust reward value calculation to accommodate for decreasing crypto value and better reward exchange requests --- curation-bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index c106880..c62b5b2 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1969,7 +1969,7 @@ function processVotes(query, subsequent) { let usd_val_no_curation = usd_val_no_benef * 0.75 / 0.65 //final upvote value after avoiding deductions - let usd_val = usd_val_no_benef / 0.65 + let usd_val = usd_val_no_benef / 0.5 //emulate proper voting power to give user matching rewards let user_added_vote_weight = usd_val * 100 / full_vote_value; @@ -2041,7 +2041,7 @@ function processVotes(query, subsequent) { let usd_val_no_curation = usd_val_no_benef * 0.75 / 0.65 //final upvote value after avoiding deductions - let usd_val = usd_val_no_benef / 0.65 + let usd_val = usd_val_no_benef / 0.5 //emulate proper voting power to give user matching rewards let user_added_vote_weight = usd_val * 100 / full_vote_value; From 1e7c4f0e958651fe47f52ad1a752385c4a9147bf Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 8 Apr 2020 19:02:56 +0300 Subject: [PATCH 149/193] Create reset login functionality Create reset login functionality --- app.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index eac2619..cb191c5 100644 --- a/app.js +++ b/app.js @@ -122,6 +122,7 @@ async function disableUserLogin(){ //console.log(result); } + async function loadAccountData(bchain){ //load main account data @@ -207,7 +208,7 @@ generatePassword = function (multip) { return passString; }; - + var bodyParser = require('body-parser'); app.use(bodyParser.json()); // support json encoded bodies @@ -305,7 +306,7 @@ let checkHdrs = (req, res, next) => { } else { return res.json({ success: false, - message: 'Auth token is not supplied' + message: 'Auth token is not provided' }); } }; @@ -382,6 +383,12 @@ app.get('/fetchUserData', checkHdrs, async function (req, res) { }); +app.get('/resetLogin', checkHdrs, async function (req, res) { + let db_col = db.collection('user_login_token'); + let result = await db_col.remove({user: req.query.user, token: req.query.token}); + res.send({success: true}); +}); + app.get('/updateSettings/', checkHdrs, async function (req, res) { let newSettings; if (req.query && req.query.user && req.query.settings){ @@ -3291,6 +3298,7 @@ app.get("/downEbook", async function(req, res) { //function handles the process of confirming payment receipt, and then proceeds with account creation, reward and delegation app.get('/confirmPayment', async function(req,res){ if (req.query.confirm_payment_token != config.confirmPaymentToken){ + //if (false){ res.send('{}'); }else{ let paymentReceivedTx = ''; From 70306513d1cc9c6638f843b56eecd924cd255828 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 13 Apr 2020 14:02:00 +0300 Subject: [PATCH 150/193] Updating weekly delegation reward calculation Updating weekly delegation reward calculation to properly reflect rewards on hive and steem separately. --- delegations.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/delegations.js b/delegations.js index 3e848e7..6db05df 100644 --- a/delegations.js +++ b/delegations.js @@ -521,7 +521,7 @@ async function getBenefactorPosts (account, start) { console.log('curAfitPrice:'+curAFITPrice.unit_price_usd); // Query account history for delegations - properties = await client.database.getDynamicGlobalProperties() + properties = await nodeLink.database.getDynamicGlobalProperties() totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) totalVests = Number(properties.total_vesting_shares.split(' ')[0]) //console.log(properties); @@ -803,7 +803,7 @@ async function processSteemRewards (chain, nodeLink, dbDelegLink, delTrxCol, act Promise.all( [ getAcumulatedSteemPower(nodeLink, dbDelegLink, delTrxCol, activeDelColLink, from, to, config.exclude_enabled), //(nodeLink, dbDelegLink, delTrxCol, activeDelColLink, start, end, config.exclude_enabled); - getBenefactorRewards(to, start, -1) + getBenefactorRewards(nodeLink, to, start, -1) ] ).then(values => { const activeDelegations = values[0].users @@ -969,7 +969,7 @@ async function processDelegations (nodeLink, dbDelegLink, delTrxCol, activeDelCo } } -async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { +async function getBenefactorRewards (nodeLink, start, end, txStart, totalSp, totalSBD) { if (!totalSBD) totalSBD = 0 if (!totalSp) totalSp = 0 let limit = (txStart < 0) ? 10000 : Math.min(txStart, 10000) @@ -978,10 +978,10 @@ async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { console.log(start) console.log(end) // Query account history for delegations - properties = await client.database.getDynamicGlobalProperties() + properties = await nodeLink.database.getDynamicGlobalProperties() totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]) totalVests = Number(properties.total_vesting_shares.split(' ')[0]) - const transactions = await client.database.call('get_account_history', [config.pay_account, txStart, limit]) + const transactions = await nodeLink.database.call('get_account_history', [config.pay_account, txStart, limit]) transactions.reverse() for (let txs of transactions) { let date = moment(txs[1].timestamp).format() @@ -1002,7 +1002,7 @@ async function getBenefactorRewards (start, end, txStart, totalSp, totalSBD) { let lastTx = transactions[transactions.length - 1] let lastDate = moment(lastTx[1].timestamp).format() // console.log(lastDate) - if (lastDate >= start) return getBenefactorRewards(start, totalSp, lastTx[0]) + if (lastDate >= start) return getBenefactorRewards(nodeLink, start, totalSp, lastTx[0]) console.log('-- Processed rewards ---') // console.log(totalSp.toFixed(3)) From 980150b46c5974b4059a880fc5d2c284586c571a Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Tue, 14 Apr 2020 12:10:01 +0300 Subject: [PATCH 151/193] Append claiming rewards for both chains Adjust claiming rewards to function properly for both chains --- curation-bot.js | 478 +++++++++++++++++++++++++++--------------------- 1 file changed, 272 insertions(+), 206 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index c62b5b2..904e521 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1,5 +1,9 @@ var fs = require("fs"); const steem = require('steem'); + +//const hive = require('steem'); + + var utils = require('./utils'); var mail = require('./mail'); var _ = require('lodash'); @@ -12,6 +16,7 @@ const axios = require('axios'); const request = require("request"); var account = null; +let actSteemAccount = null; var last_trans = 0; var members = []; var whitelist = []; @@ -201,7 +206,7 @@ async function fetchGadgets(){ activeGadgets = fetch_gadgets_res.data; gadgetsFetched = true; - console.log(activeGadgets); + //console.log(activeGadgets); }catch(err){ console.log(err); } @@ -315,7 +320,6 @@ MongoClient.connect(url, function(err, client) { }); - // Schedule to run every minute if (!config.testing){ setInterval(startProcess, 60 * 1000); @@ -670,9 +674,27 @@ async function startProcess() { utils.log('Start process'); // Load the settings from the config file each time so we can pick up any changes loadConfig(); - + + + //load steem account data + + await steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + + await steem.api.getAccounts([config.account], function (err, result) { + if (err || !result) + utils.log(err, result); + else { + actSteemAccount = result[0]; + //console.log(actSteemAccount); + } + }); + + - steem.api.setOptions({ + await steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true }); @@ -683,7 +705,7 @@ async function startProcess() { utils.log(err, result); else { account = result[0]; - + //console.log(account); } }); @@ -713,7 +735,7 @@ async function startProcess() { console.log('skip:'+skip); console.log('is_voting:'+is_voting); console.log('passedOneDay:'+passedOneDay); - + //BuyAndBurn(true); @@ -726,9 +748,9 @@ async function startProcess() { let vpRestart = parseFloat(config.vp_kickstart) - 125; let vpRestartLimit = vpRestart + 2; console.log('vpRestart:'+vpRestart); - + utils.log('Voting Power: ' + utils.format(vp) + '% | Time until next vote: ' + utils.toTimer(utils.timeTilFullPower(vp*100))); - + //disabling buying and burning tokens /*console.log(config.vpTokenBurn); @@ -773,7 +795,7 @@ async function startProcess() { // Check if there are any rewards to claim before voting if (!config.testing){ - claimRewards(); + await claimRewards(); } //fetch gadget assignments @@ -832,7 +854,7 @@ async function startProcess() { console.log('skippable_posts'); console.log(skippable_posts); - + //grab list of active gadgets //fetchGadgets(); @@ -923,7 +945,9 @@ function BuyAndBurn(test){ function setSteemPrice(json){ steem_price = parseFloat(json.steem.usd); console.log('STEEM price:'+steem_price) - broadcastFeed('STEEM') + if (!config.testing){ + broadcastFeed('STEEM') + } } function setSbdPrice(json){ @@ -934,7 +958,9 @@ function setSbdPrice(json){ function setHivePrice(json){ hive_price = parseFloat(json.hive.usd); console.log('HIVE price:'+hive_price) - broadcastFeed('HIVE') + if (!config.testing){ + broadcastFeed('HIVE') + } } function setHbdPrice(json){ @@ -1066,6 +1092,7 @@ function broadcastFeed (type) { } + //var post_scores = []; function processVotes(query, subsequent) { @@ -1106,8 +1133,12 @@ function processVotes(query, subsequent) { for(var i = 0; i < result.length; i++) { var post = result[i]; - - + if (config.testing && i == 0){ + console.log('switch author'); + console.log(post.author); + post.author = 'mcfarhat'; + post.permlink = 'actifit-witness-vote-application-msp'; + } //if this is a subsequent call, we need to skip first post if (subsequent && i==0){ // utils.log('skip post:'+post.title); @@ -1132,7 +1163,7 @@ function processVotes(query, subsequent) { } // Check if the bot already voted on this post - if(post.active_votes.find(v => v.voter == 'actifit')) { + if (post.active_votes.find(v => v.voter == 'actifit')) { utils.log('Bot already voted on: ' + post.url); continue; } @@ -1223,7 +1254,7 @@ function processVotes(query, subsequent) { if (post_skippable) continue; try { - + console.log('parsing data by post '+post.url); post.json = JSON.parse(post.json_metadata); @@ -1317,24 +1348,24 @@ function processVotes(query, subsequent) { - //check if the post has an encryption key val, and ensure it is the proper one - if (post.json.actiCrVal){ - var txt_to_encr = post.author + post.permlink + post.json.step_count ; - var cipher = crypto.createCipher(config.encr_mode, config.encr_key); - let encr_txt = cipher.update(txt_to_encr, 'utf8', 'hex'); - encr_txt += cipher.final('hex'); - //test the result to the post's relevant data - if (post.json.actiCrVal != encr_txt){ - //wrong, skip post - utils.log('post has incorrect actiCrVal'); + //check if the post has an encryption key val, and ensure it is the proper one + if (post.json.actiCrVal){ + var txt_to_encr = post.author + post.permlink + post.json.step_count ; + var cipher = crypto.createCipher(config.encr_mode, config.encr_key); + let encr_txt = cipher.update(txt_to_encr, 'utf8', 'hex'); + encr_txt += cipher.final('hex'); + //test the result to the post's relevant data + if (post.json.actiCrVal != encr_txt){ + //wrong, skip post + utils.log('post has incorrect actiCrVal'); + continue; + } + //utils.log('post is valid'); + }else{ + utils.log('post does not contain actiCrVal'); continue; } - //utils.log('post is valid'); - }else{ - utils.log('post does not contain actiCrVal'); - continue; - } - + } //console.log('still here'); @@ -1368,43 +1399,43 @@ function processVotes(query, subsequent) { }else{ if (first_index!=last_index) { utils.log('---- User already has more than 2 posts in 24 hours ------'); - var last_date = moment(last_voted.created).format('D'); - let first_voted = votePosts[first_index]; - var first_date = moment(first_voted.created).format('D'); - var this_date = moment(post.created).format('D'); - //if all 3 dates match, skip it - if ((last_date == this_date) && (first_date == this_date)) { - utils.log('---- Last voted -----'); - utils.log(new Date (last_voted.created)); - utils.log('---- First voted -----'); - utils.log(new Date (first_voted.created)); - utils.log('---- This voted -----'); - utils.log(new Date (post.created)); - utils.log('---- Moment-----'); - utils.log(last_date); - utils.log(first_date); - utils.log(this_date); - - //skip new post - skip_date_diff = true; - } + var last_date = moment(last_voted.created).format('D'); + let first_voted = votePosts[first_index]; + var first_date = moment(first_voted.created).format('D'); + var this_date = moment(post.created).format('D'); + //if all 3 dates match, skip it + if ((last_date == this_date) && (first_date == this_date)) { + utils.log('---- Last voted -----'); + utils.log(new Date (last_voted.created)); + utils.log('---- First voted -----'); + utils.log(new Date (first_voted.created)); + utils.log('---- This voted -----'); + utils.log(new Date (post.created)); + utils.log('---- Moment-----'); + utils.log(last_date); + utils.log(first_date); + utils.log(this_date); + + //skip new post + skip_date_diff = true; + } }else{ utils.log('reject a post if a prior one exists that is less than 6 hours away'); - utils.log(post.author+post.url); - //adding condition to reject a post if a prior one exists that is less than 6 hours away - //utils.log(last_voted.author+last_voted.url); - var last_date = moment(last_voted.created).toDate(); - var this_date = moment(post.created).toDate(); - //check the hours difference - var hours_diff = Math.abs(this_date - last_date) / 36e5; - if (hours_diff user_post.author === cur_upvote_entry.user); - - //decrease by 1% since assisting accounts will vote too (pay & funds) only if we still have room to use them - if (helping_accounts_votes < config.max_helping_votes){ - user_added_vote_weight -= 1; - helping_accounts_votes += 1; + //emulate proper voting power to give user matching rewards + let user_added_vote_weight = usd_val * 100 / full_vote_value; - votePosts[entry_index].helperVotes = true; - } - - user_added_vote_weight = user_added_vote_weight.toFixed(2); - - votePosts[entry_index].additional_vote_weight = Math.floor(user_added_vote_weight * 100); - votePosts[entry_index].afit_swapped = cur_upvote_entry.paid_afit; - console.log('Additional Vote Weight for AFIT/STEEM Upvote Exchange: '+votePosts[entry_index].author + ' ' + votePosts[entry_index].url); - console.log(votePosts[entry_index].additional_vote_weight); - - //we need to set params of this transaction - cur_upvote_entry.additional_vote_weight = votePosts[entry_index].additional_vote_weight / 100; - cur_upvote_entry.usd_val_no_benef = +usd_val_no_benef; - cur_upvote_entry.usd_val_no_curation = +usd_val_no_curation.toFixed(2); - cur_upvote_entry.usd_val = +usd_val.toFixed(2); - - cur_upvote_entry.reward_cycle_ID = reward_cycle_ID; - - cur_upvote_entry.post_author = votePosts[entry_index].author; - cur_upvote_entry.post_permlink = votePosts[entry_index].permlink; - - db.collection('exchange_afit_steem').save(cur_upvote_entry); + let entry_index = votePosts.findIndex( user_post => user_post.author === cur_upvote_entry.user); + + //decrease by 1% since assisting accounts will vote too (pay & funds) only if we still have room to use them + if (helping_accounts_votes < config.max_helping_votes){ + user_added_vote_weight -= 1; + + helping_accounts_votes += 1; + + votePosts[entry_index].helperVotes = true; + } + + user_added_vote_weight = user_added_vote_weight.toFixed(2); + + votePosts[entry_index].additional_vote_weight = Math.floor(user_added_vote_weight * 100); + votePosts[entry_index].afit_swapped = cur_upvote_entry.paid_afit; + console.log('Additional Vote Weight for AFIT/STEEM Upvote Exchange: '+votePosts[entry_index].author + ' ' + votePosts[entry_index].url); + console.log(votePosts[entry_index].additional_vote_weight); + + //we need to set params of this transaction + cur_upvote_entry.additional_vote_weight = votePosts[entry_index].additional_vote_weight / 100; + cur_upvote_entry.usd_val_no_benef = +usd_val_no_benef; + cur_upvote_entry.usd_val_no_curation = +usd_val_no_curation.toFixed(2); + cur_upvote_entry.usd_val = +usd_val.toFixed(2); + + cur_upvote_entry.reward_cycle_ID = reward_cycle_ID; + + cur_upvote_entry.post_author = votePosts[entry_index].author; + cur_upvote_entry.post_permlink = votePosts[entry_index].permlink; + + db.collection('exchange_afit_steem').save(cur_upvote_entry); } } } @@ -2175,7 +2206,7 @@ function votingProcess(posts, power_per_vote) { //since we're done voting, we need to update all user tokens to reflect new rewards if (!config.testing){ - updateUserTokens(); + updateUserTokens(); } //reportEmail(config.report_emails) }, config.voting_posting_delay); @@ -2188,7 +2219,7 @@ function votingProcess(posts, power_per_vote) { async function sendVote(post, retries, power_per_vote) { - + utils.log('Voting on: ' + post.url + ' with count'+post.json.step_count); var token_count = post.post_score;//parseFloat(post.rate_multiplier)*100; @@ -2342,10 +2373,10 @@ async function sendVote(post, retries, power_per_vote) { url: config.active_hive_node , //useAppbaseApi: true }); - //vote first using pay and funds accounts only if we have an AFIT/STEEM exchange operation and we have room to upvote using helping accounts - if (post.additional_vote_weight && post.helperVotes){ - let vote_percent_add_accounts = config.helping_account_percent;//at 50%: 5000 - try{ + //vote first using pay and funds accounts only if we have an AFIT/STEEM exchange operation and we have room to upvote using helping accounts + if (post.additional_vote_weight && post.helperVotes){ + let vote_percent_add_accounts = config.helping_account_percent;//at 50%: 5000 + try{ utils.log('voting with '+config.full_pay_benef_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); steem.api.setOptions({ @@ -2372,13 +2403,13 @@ async function sendVote(post, retries, power_per_vote) { if (res && res.block_num) { utils.log('success'); } - - }catch(err){ - utils.log(err); - } + + }catch(err){ + utils.log(err); + } - try{ - utils.log('voting with '+config.pay_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); + try{ + utils.log('voting with '+config.pay_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true @@ -2403,17 +2434,17 @@ async function sendVote(post, retries, power_per_vote) { if (res && res.block_num) { utils.log('success'); } - - }catch(err){ - utils.log(err); - } - } - - //if additional partner accounts enabled, vote using them as well - try{ - if (config.zzan_active){ - utils.log('voting with '+config.zzan_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); + }catch(err){ + utils.log(err); + } + + } + + //if additional partner accounts enabled, vote using them as well + try{ + if (config.zzan_active){ + utils.log('voting with '+config.zzan_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true @@ -2438,8 +2469,8 @@ async function sendVote(post, retries, power_per_vote) { if (res && res.block_num) { utils.log('success'); } - - } + + } }catch(err){ utils.log(err); } @@ -2450,7 +2481,7 @@ async function sendVote(post, retries, power_per_vote) { steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); + }); res = await steem.broadcast.sendAsync( { operations: [ @@ -2472,16 +2503,16 @@ async function sendVote(post, retries, power_per_vote) { utils.log('success'); } + } + }catch(err){ + utils.log(err); } - }catch(err){ - utils.log(err); - } - + //append appics account gadget-based voting if (config.appics_active && post.vote_appics){ - try{ + try{ utils.log('voting with '+config.appics_account+ ' '+utils.format(post.boost_apx_percent / 100) + '% vote cast for: ' + post.url); steem.api.setOptions({ url: config.active_hive_node , @@ -2507,7 +2538,7 @@ async function sendVote(post, retries, power_per_vote) { if (res && res.block_num) { utils.log('success'); } - + }catch(err){ utils.log(err); } @@ -2518,7 +2549,7 @@ async function sendVote(post, retries, power_per_vote) { steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); + }); res = await steem.broadcast.sendAsync( { operations: [ @@ -2528,7 +2559,7 @@ async function sendVote(post, retries, power_per_vote) { "author": post.author, "permlink": post.permlink, "weight": net_rewards_vote_weight - } + } ] ], extensions: [] @@ -2540,10 +2571,10 @@ async function sendVote(post, retries, power_per_vote) { utils.log('success'); } - }catch(err){ - utils.log(err); - } - + }catch(err){ + utils.log(err); + } + try{ utils.log('voting with '+account.name+ ' '+utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); steem.api.setOptions({ @@ -2566,20 +2597,20 @@ async function sendVote(post, retries, power_per_vote) { }, { posting: config.posting_key } ).then(async function(rest, err) { - - //store exchange transaction as complete - if (post.additional_vote_weight){ - console.log('Exchange vote'); - let cur_upvote_entry = afit_steem_upvote_list.find( entry => entry.post_author === post.author && - entry.post_permlink === post.permlink && - entry.user === post.author); - console.log(cur_upvote_entry); - cur_upvote_entry.upvote_processed = true; - db.collection('exchange_afit_steem').save(cur_upvote_entry); - } - - if(config.comment_location && config.comment){ + //store exchange transaction as complete + if (post.additional_vote_weight){ + console.log('Exchange vote'); + let cur_upvote_entry = afit_steem_upvote_list.find( entry => entry.post_author === post.author && + entry.post_permlink === post.permlink && + entry.user === post.author); + console.log(cur_upvote_entry); + cur_upvote_entry.upvote_processed = true; + db.collection('exchange_afit_steem').save(cur_upvote_entry); + } + + + if(config.comment_location && config.comment){ await sendComment(post, 0, vote_weight, config.active_hive_node) .then( res => { //resolve(res) @@ -2620,16 +2651,16 @@ async function sendVote(post, retries, power_per_vote) { // Try again one time on error /*if (retries < config.max_vote_comment_retries){ //try to vote again - setTimeout(function () { + setTimeout(function () { sendVote(post, retries + 1, power_per_vote) - .then( res => { - resolve(res) - }) - .catch(err => { - reject(err); - }) - }, config.voting_posting_delay); - }else{ + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, config.voting_posting_delay); + }else { var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' utils.log(message); reject(err); @@ -2786,7 +2817,7 @@ async function sendVote(post, retries, power_per_vote) { if (res && res.block_num) { utils.log('success'); } - + } }catch(err){ utils.log(err); @@ -2909,7 +2940,7 @@ async function sendVote(post, retries, power_per_vote) { //reject(err); }) //}, config.voting_posting_delay); - }else{ + }else{ //resolve(''); } }).catch(e => console.log(e)) @@ -2927,34 +2958,34 @@ async function sendVote(post, retries, power_per_vote) { cur_upvote_entry.upvote_processed = true; db.collection('exchange_afit_steem').save(cur_upvote_entry); }*/ - - + + }else{ - // Try again one time on error + // Try again one time on error /*if (retries < config.max_vote_comment_retries){ - //try to vote again - setTimeout(function () { - sendVote(post, retries + 1, power_per_vote) - .then( res => { - resolve(res) - }) - .catch(err => { - reject(err); - }) - }, config.voting_posting_delay); - }else { - var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' - utils.log(message); - reject(err); - //errorEmail(message, config.report_emails); + //try to vote again + setTimeout(function () { + sendVote(post, retries + 1, power_per_vote) + .then( res => { + resolve(res) + }) + .catch(err => { + reject(err); + }) + }, config.voting_posting_delay); + }else { + var message = '============= Vote transaction failed '+retries+' times for: ' + post.url + ' ===============' + utils.log(message); + reject(err); + //errorEmail(message, config.report_emails); }*/ } }catch(mainerr){ utils.log(mainerr); - } - } + + } resolve(''); } }); @@ -3140,7 +3171,7 @@ async function sendComment(post, retries, vote_weight, bchain_node) { utils.log('Posted comment: ' + permlink); //resolve(res); } - + /*steem.broadcast.comment(config.posting_key, parentAuthor, parentPermlink, account.name, permlink, permlink, content, jsonMetadata, function (err, result) { if (!err && result) { utils.log('Posted comment: ' + permlink); @@ -3166,7 +3197,7 @@ async function sendComment(post, retries, vote_weight, bchain_node) { }catch(com_err){ utils.log(com_err); } - + }else{ utils.log('comment'); console.log(content); @@ -3274,39 +3305,74 @@ function sendPayment(to, amount, currency, reason, retries, data) { }); } -function claimRewards() { +async function claimRewards(target_chain) { + console.log('>>>>> claiming rewards'); if (!config.auto_claim_rewards) return; - + + let targetAccount = actSteemAccount; + + let claim_currency = targetAccount.reward_steem_balance + let claim_currency_stable = targetAccount.reward_sbd_balance + + if (target_chain == 'HIVE'){ + targetAccount = account; + claim_currency = targetAccount.reward_steem_balance.replace("HIVE", "STEEM"); + claim_currency_stable = targetAccount.reward_sbd_balance.replace("HBD", "SBD"); + + } // Make api call only if you have actual reward - if (parseFloat(account.reward_steem_balance) > 0 || parseFloat(account.reward_sbd_balance) > 0 || parseFloat(account.reward_vesting_balance) > 0) { - steem.broadcast.claimRewardBalance(config.posting_key, config.account, account.reward_steem_balance, account.reward_sbd_balance, account.reward_vesting_balance, function (err, result) { + if (parseFloat(targetAccount.reward_steem_balance) > 0 || parseFloat(targetAccount.reward_sbd_balance) > 0 || parseFloat(targetAccount.reward_vesting_balance) > 0) { + if (!target_chain){ + await steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + }); + }else{ + await steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + }); + } + await steem.broadcast.claimRewardBalance(config.posting_key, config.account, claim_currency, claim_currency_stable, targetAccount.reward_vesting_balance, function (err, result) { if (err) { + console.log('error claiming rewards'); utils.log(err); } if (result) { var rewards_message = "$$$ ==> Rewards Claim"; - if (parseFloat(account.reward_sbd_balance) > 0) { rewards_message = rewards_message + ' SBD: ' + parseFloat(account.reward_sbd_balance); } - if (parseFloat(account.reward_steem_balance) > 0) { rewards_message = rewards_message + ' STEEM: ' + parseFloat(account.reward_steem_balance); } - if (parseFloat(account.reward_vesting_balance) > 0) { rewards_message = rewards_message + ' VESTS: ' + parseFloat(account.reward_vesting_balance); } + if (parseFloat(targetAccount.reward_sbd_balance) > 0) { rewards_message = rewards_message + ' SBD: ' + parseFloat(targetAccount.reward_sbd_balance); } + if (parseFloat(targetAccount.reward_steem_balance) > 0) { rewards_message = rewards_message + ' STEEM: ' + parseFloat(targetAccount.reward_steem_balance); } + if (parseFloat(targetAccount.reward_vesting_balance) > 0) { rewards_message = rewards_message + ' VESTS: ' + parseFloat(targetAccount.reward_vesting_balance); } utils.log(rewards_message); + + //now attempt to claim rewards with HIVE + if (!target_chain){ + claimRewards('HIVE'); + } // If there are liquid post rewards, withdraw them to the specified account - if (parseFloat(account.reward_sbd_balance) > 0 && config.post_rewards_withdrawal_account && config.post_rewards_withdrawal_account != '') { + if (parseFloat(targetAccount.reward_sbd_balance) > 0 && config.post_rewards_withdrawal_account && config.post_rewards_withdrawal_account != '') { // Send liquid post rewards to the specified account - steem.broadcast.transfer(config.active_key, config.account, config.post_rewards_withdrawal_account, account.reward_sbd_balance, 'Liquid Post Rewards Withdrawal', function (err, response) { + steem.broadcast.transfer(config.active_key, config.account, config.post_rewards_withdrawal_account, targetAccount.reward_sbd_balance, 'Liquid Post Rewards Withdrawal', function (err, response) { if (err){ utils.log(err, response); }else{ - utils.log('$$$ Auto withdrawal - liquid post rewards: ' + account.reward_sbd_balance + ' sent to @' + config.post_rewards_withdrawal_account); + utils.log('$$$ Auto withdrawal - liquid post rewards: ' + targetAccount.reward_sbd_balance + ' sent to @' + config.post_rewards_withdrawal_account); } }); } } }); + }else{ + console.log('nothing to claim. Try other chain if possible'); + //now attempt to claim rewards with HIVE + if (!target_chain){ + claimRewards('HIVE'); + } } } From 31796004df8e4e8676faa84daf664e5e41940200 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 17 Apr 2020 19:02:43 +0300 Subject: [PATCH 152/193] Improve error reporting based on trx Implement better error reporting to ensure failed trx do not return as successful --- app.js | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index cb191c5..d1a120d 100644 --- a/app.js +++ b/app.js @@ -312,6 +312,48 @@ let checkHdrs = (req, res, next) => { }; +app.post('/performTrxPost', checkHdrs, async function (req, res) { + console.log('>>performTrx'); + + + const receivedPlaintext = decrypt(req.ppkey); + + //set HIVE as default + let bchain = 'HIVE'; + + let userKey = receivedPlaintext; + + let operation; + console.log(req.body); + console.log(req.body.operation); + if (req.body && req.body.operation){ + operation = JSON.parse(req.body.operation); + //operation = req.query.operation; + }else{ + res.send({error: 'operation not supplied'}); + } + + if (req.query.bchain){ + bchain = req.query.bchain; + } + + let match_arr = Object.entries(operation); + /*console.log(user); + console.log(operation); + console.log((typeof operation)); + console.log(match_arr); + console.log(match_arr[0][1]);*/ + + //perform transaction + let performTrx = await utils.processSteemTrx(match_arr[0][1], userKey, bchain); + console.log(performTrx); + if (!performTrx.tx.block_num){ + res.send({error: true, trx: performTrx}); + }else{ + res.send({success: true, trx: performTrx}); + } +}); + app.get('/performTrx', checkHdrs, async function (req, res) { console.log('>>performTrx'); @@ -346,7 +388,11 @@ app.get('/performTrx', checkHdrs, async function (req, res) { //perform transaction let performTrx = await utils.processSteemTrx(match_arr[0][1], userKey, bchain); console.log(performTrx); - res.send({success: true, trx: performTrx}); + if (!performTrx.tx.block_num){ + res.send({error: true, trx: performTrx}); + }else{ + res.send({success: true, trx: performTrx}); + } }); app.get('/fetchUserData', checkHdrs, async function (req, res) { @@ -1407,13 +1453,16 @@ app.get('/isoParticipantList/', async function (req, res) { /* end point for returning current active delegator data by actifit */ app.get('/topDelegators', async function (req, res) { - var delegatorList; + let delegatorList; + let hiveDelegatorList; if (isNaN(req.query.count)){ delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).toArray(); + hiveDelegatorList = await db.collection('hive_active_delegations').find().sort({steem_power: -1}).toArray(); }else{ delegatorList = await db.collection('active_delegations').find().sort({steem_power: -1}).limit(parseInt(req.query.count)).toArray(); + hiveDelegatorList = await db.collection('hive_active_delegations').find().sort({steem_power: -1}).limit(parseInt(req.query.count)).toArray(); } - res.send(delegatorList); + res.send({steem: delegatorList, hive: hiveDelegatorList}); }); activeDelegationFunc = async function (userName){ @@ -4141,6 +4190,26 @@ app.get('/totalPostsSubmitted', async function(req, res) { }); +/* end point for fetching user's recorded metrics */ +app.get('/trackedMeasurements/:user', async function(req, res) { + let query = {"author": req.params.user, + $or: [ + { "json_metadata.weight": {$exists: true} }, + { "json_metadata.height": {$exists: true} }, + { "json_metadata.chest": {$exists: true} }, + { "json_metadata.waist": {$exists: true} }, + { "json_metadata.thighs": {$exists: true} }, + { "json_metadata.bodyfat": {$exists: true} } + ] + } + posts = await db.collection('verified_posts').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + res.send(posts); +}); + + + + + function gk_add_commas(nStr) { if (isNaN(nStr)){ From 4c3889b05b4e31480f8ecdb2b995a263b37bc760 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 18 Apr 2020 19:42:56 +0300 Subject: [PATCH 153/193] Append login history Append login history + run on single node to avoid duplication of effort and collision --- app.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index d1a120d..0ed65f0 100644 --- a/app.js +++ b/app.js @@ -49,7 +49,7 @@ MongoClient.connect(url, function(err, client) { // Get the documents collection collection = db.collection(collection_name); - disableUserLogin(); + //disableUserLogin(); } else { utils.log(err, 'api'); @@ -75,7 +75,10 @@ let scJob = schedule.scheduleJob('*/5 * * * *', async function(){ //usersAFITXBal = []; fetchAFITXBal(0); - disableUserLogin(); + //only run cleanup on secondary thread to avoid duplication of effort and collision + if (process.env.BOT_THREAD == 'SECOND_API'){ + disableUserLogin(); + } }); //allows setting acceptable origins to be included across all function calls @@ -113,12 +116,24 @@ loadAccountData(); async function disableUserLogin(){ console.log('check outdated logins'); let db_col = db.collection('user_login_token'); + let db_hist_col = db.collection('user_login_history'); let dateTarget = new Date(); - //allow logins to remain valid for 4 hours - dateTarget.setHours(dateTarget.getHours()-4); + //allow logins to remain valid for 12 hours + dateTarget.setHours(dateTarget.getHours()-12); console.log(dateTarget); - //find existing login entry in DB to override - let result = await db_col.remove({lastlogin: {$lt: dateTarget }}); + //find existing login entry in DB to move to history and then remove + let items_to_move = await db_col.find({lastlogin: {$lt: dateTarget }}).toArray(); + if (Array.isArray(items_to_move) && items_to_move.length > 0){ + console.log('moving and deleting '+ items_to_move.length + ' old logins'); + //cleanup data to prevent keeping keys + items_to_move.forEach(function(ent){ delete ent.ppkey; delete ent.token }); + + await db_hist_col.insert(items_to_move); + let result = await db_col.remove({lastlogin: {$lt: dateTarget }}); + }else{ + console.log('noting to clean'); + } + //console.log(result); } From 57b40d723ec82e00fc41694dd80eac3212f21972 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 29 Apr 2020 18:00:10 +0300 Subject: [PATCH 154/193] Add support for running gadget trx on hive Add support for running gadget trx on hive --- app.js | 12 ++++++------ utils.js | 13 ++++++++++--- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/app.js b/app.js index 0ed65f0..deda9a9 100644 --- a/app.js +++ b/app.js @@ -1041,10 +1041,10 @@ app.get('/markRead/:notif_id', async function (req, res) { }); /* end point for tracking gadget buy orders */ -app.get('/buyGadget/:user/:gadget/:blockNo/:trxID', async function (req, res) { +app.get('/buyGadget/:user/:gadget/:blockNo/:trxID/:bchain', async function (req, res) { //ensure proper transaction - let ver_trx = await utils.verifyGadgetTransaction(req.params.user, req.params.gadget, 'buy-gadget', req.params.blockNo, req.params.trxID); + let ver_trx = await utils.verifyGadgetTransaction(req.params.user, req.params.gadget, 'buy-gadget', req.params.blockNo, req.params.trxID, req.params.bchain); if (!ver_trx){ res.send({status: 'error'}); return; @@ -3169,7 +3169,7 @@ app.get("/gadgetBought", async function(req, res) { //end point handles activating a bought gadget -app.get('/activateGadget/:user/:gadget/:blockNo/:trxID/:benefic?', async function (req, res) { +app.get('/activateGadget/:user/:gadget/:blockNo/:trxID/:bchain/:benefic?', async function (req, res) { let user = req.params.user; let gadget = req.params.gadget; @@ -3180,7 +3180,7 @@ app.get('/activateGadget/:user/:gadget/:blockNo/:trxID/:benefic?', async functio } console.log('activateGadget'); - let ver_trx = await utils.verifyGadgetTransaction(user, gadget, 'activate-gadget', req.params.blockNo, req.params.trxID); + let ver_trx = await utils.verifyGadgetTransaction(user, gadget, 'activate-gadget', req.params.blockNo, req.params.trxID, req.params.bchain); console.log(ver_trx); //ensure proper transaction if (!ver_trx){ @@ -3205,12 +3205,12 @@ app.get('/activateGadget/:user/:gadget/:blockNo/:trxID/:benefic?', async functio }); //end point handles deactivating a bought gadget -app.get('/deactivateGadget/:user/:gadget/:blockNo/:trxID', async function (req, res) { +app.get('/deactivateGadget/:user/:gadget/:blockNo/:bchain/:trxID', async function (req, res) { let user = req.params.user; let gadget = req.params.gadget; //ensure proper transaction - let ver_trx = await utils.verifyGadgetTransaction(user, gadget, 'deactivate-gadget', req.params.blockNo, req.params.trxID); + let ver_trx = await utils.verifyGadgetTransaction(user, gadget, 'deactivate-gadget', req.params.blockNo, req.params.trxID, req.params.bchain); if (!ver_trx){ res.send({status: 'error'}); return; diff --git a/utils.js b/utils.js index aed3fa2..8fc8394 100644 --- a/utils.js +++ b/utils.js @@ -79,7 +79,7 @@ var HOURS = 60 * 60; console.log('utils processSteemTrx'); console.log(operation); const ops = [ operation ]; - console.log('>>>>>>>>>>>> selected bchain'); + console.log('>>>>>>>>>>>> selected bchain <<<<<<<<<<'); console.log(bchain); await setProperNode(bchain); let tx = await steem.broadcast.sendAsync( @@ -1268,9 +1268,16 @@ async function rewardPost(post_url, vp, bchain){ return result; } -async function verifyGadgetTransaction(userA, gadget_id, tx_type, block_num, tx_id){ - let trx = await client.database.getTransaction({id: tx_id, block_num: block_num}); +async function verifyGadgetTransaction(userA, gadget_id, tx_type, block_num, tx_id, bchain){ + let trx; + console.log('verifyGadgetTransaction'); try{ + if (bchain == 'STEEM'){ + trx = await client.database.getTransaction({id: tx_id, block_num: block_num}); + }else if (bchain == 'HIVE'){ + trx = await hiveClient.database.getTransaction({id: tx_id, block_num: block_num}); + } + console.log(trx); if (trx && trx.operations && trx.operations.length > 0){ console.log(trx.operations[0][1]); From 9a8bfdddad319fa79ee5b91c1fd3bb5969129644 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 29 Apr 2020 19:05:51 +0300 Subject: [PATCH 155/193] Fix ordering issue in API call Fix ordering issue in API call --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index deda9a9..9d60d5a 100644 --- a/app.js +++ b/app.js @@ -3205,7 +3205,7 @@ app.get('/activateGadget/:user/:gadget/:blockNo/:trxID/:bchain/:benefic?', async }); //end point handles deactivating a bought gadget -app.get('/deactivateGadget/:user/:gadget/:blockNo/:bchain/:trxID', async function (req, res) { +app.get('/deactivateGadget/:user/:gadget/:blockNo/:trxID/:bchain', async function (req, res) { let user = req.params.user; let gadget = req.params.gadget; From 3f2d326c3bc4f64cbc052c9bb40d536c45956aaa Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 30 Apr 2020 01:59:58 +0300 Subject: [PATCH 156/193] implement keep logged in API support implement keep logged in API support --- app.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app.js b/app.js index 9d60d5a..6804b0f 100644 --- a/app.js +++ b/app.js @@ -121,8 +121,8 @@ async function disableUserLogin(){ //allow logins to remain valid for 12 hours dateTarget.setHours(dateTarget.getHours()-12); console.log(dateTarget); - //find existing login entry in DB to move to history and then remove - let items_to_move = await db_col.find({lastlogin: {$lt: dateTarget }}).toArray(); + //find existing login entry in DB to move to history and then remove .. only if user wants to get logged out + let items_to_move = await db_col.find({lastlogin: {$lt: dateTarget }, keeploggedin: {$ne: true}}).toArray(); if (Array.isArray(items_to_move) && items_to_move.length > 0){ console.log('moving and deleting '+ items_to_move.length + ' old logins'); //cleanup data to prevent keeping keys @@ -522,7 +522,10 @@ app.post('/loginAuth', async function (req, res) { user_tkn.token = token; user_tkn.ppkey = ciphertext; user_tkn.lastlogin = new Date(); - + //keep record free from deletion on cleanup + if (req.body && req.body.keeploggedin){ + user_tkn.keeploggedin = req.body.keeploggedin; + } let db_save = await db_col.save(user_tkn); // return the JWT token for the future API calls From 7af815ef6c04aaa4c3ce047e7b60873c93be13ce Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 30 Apr 2020 15:21:45 +0300 Subject: [PATCH 157/193] Create beneficiary notification Create notification to users upon being set as beneficiary for gadget rewards by another user --- app.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app.js b/app.js index 6804b0f..ee1cc5b 100644 --- a/app.js +++ b/app.js @@ -3198,6 +3198,9 @@ app.get('/activateGadget/:user/:gadget/:blockNo/:trxID/:bchain/:benefic?', async gadget_match.status="active"; if (req.params.benefic){ gadget_match.benefic = req.params.benefic; + + //also send notification to the beneficiary about being set for this gadget + sendNotification(req.params.benefic.replace('@',''), user, 'gadget_beneficiary', 'User ' + user + ' has set you as reward beneficiary for one of their gadgets!', 'https://actifit.io/'+user); } db.collection('user_gadgets').save(gadget_match); res.send({'status': 'success'}); From b6f63104212efd73ad236d93762270b00d0ef7d5 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 30 Apr 2020 15:48:46 +0300 Subject: [PATCH 158/193] Add tip notifications Add notifications to recipient users about receiving AFIT tips --- app.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index ee1cc5b..7c8f152 100644 --- a/app.js +++ b/app.js @@ -1753,7 +1753,7 @@ app.get('/initiateAFITMoveSE', async function(req, res){ }) /* function handles the processing of a buy order */ -app.get('/tipAccount', async function(req, res){ +app.get('/tipAccount', async function(req, res){ if (!req.query.user || !req.query.targetUser || !req.query.amount || !req.query.fundsPass) { //make sure all params are sent res.send({'error':'generic error'}); @@ -1865,6 +1865,8 @@ app.get('/tipAccount', async function(req, res){ return; } + //also send notification to the recipient about tipped amount + sendNotification(targetUser, user, 'tip_notification', 'User ' + user + ' has sent you a tip of '+ amount +' AFIT', 'https://actifit.io/'+user); //update sending user's token balance & store to db let new_token_count = cur_sender_token_count - amount; From b015c9643da57b7e4c1b33acf4e172a057fdfd87 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 30 Apr 2020 17:01:28 +0300 Subject: [PATCH 159/193] Hive based funds pass verification Append support for hive based funds pass verification --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 7c8f152..61ddd2b 100644 --- a/app.js +++ b/app.js @@ -3604,7 +3604,7 @@ app.get('/confirmPaymentPasswordVerify', async function(req,res){ res.write(' '); },8000); try{ - let bchain = (req.query&&req.query.bchain?req.query.bchain:''); + let bchain = (req.query&&req.query.bchain?req.query.bchain:'HIVE'); paymentReceivedTx = await utils.confirmPaymentReceivedPassword(req, bchain); console.log('>>>> got TX '+paymentReceivedTx); if (paymentReceivedTx != ''){ From b86fd4291b8f8906fce70134d0bf599edae4c554 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 1 May 2020 17:57:28 +0300 Subject: [PATCH 160/193] Refactor notifications to utils Refactor send notifications function under utils for better availability --- app.js | 28 ++++------------------------ utils.js | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/app.js b/app.js index 61ddd2b..27cb2a1 100644 --- a/app.js +++ b/app.js @@ -1006,26 +1006,6 @@ app.get('/userFriends/:user', async function (req, res) { res.send(friendsA.concat(friendsB)); }); -sendNotification = async function (user, action_taker, type, details, url){ - let notification_entry = { - user: user, - action_taker: action_taker, - type: type, - details: details, - url: url, - date: new Date(), - status: 'unread', - }; - try{ - let transaction = await db.collection('notifications').insert(notification_entry); - console.log('success inserting notification data'); - return true; - }catch(err){ - console.log('error'); - return false; - } -} - /* end point for marking a notification as read */ app.get('/markRead/:notif_id', async function (req, res) { let notif_to_update = { @@ -1220,7 +1200,7 @@ app.get('/addFriend/:userA/:userB/:blockNo/:trxID/:bchain', async function (req, console.log('success inserting post data'); //notify recipient - sendNotification(req.params.userB, req.params.userA, 'friendship_request', 'User ' + req.params.userA + ' has sent you a friendship request', 'https://actifit.io/'+req.params.userA); + utils.sendNotification(db, req.params.userB, req.params.userA, 'friendship_request', 'User ' + req.params.userA + ' has sent you a friendship request', 'https://actifit.io/'+req.params.userA); res.send({status: 'success'}); }catch(err){ @@ -1292,7 +1272,7 @@ app.get('/acceptFriend/:userA/:userB/:blockNo/:trxID/:bchain', async function (r console.log('success updating post data'); //notify recipient - sendNotification(req.params.userB, req.params.userA, 'friendship_acceptance', 'User ' + req.params.userA + ' has accepted your friendship request', 'https://actifit.io/'+req.params.userA); + utils.sendNotification(db, req.params.userB, req.params.userA, 'friendship_acceptance', 'User ' + req.params.userA + ' has accepted your friendship request', 'https://actifit.io/'+req.params.userA); insertSuccess = true; }catch(err){ @@ -1866,7 +1846,7 @@ app.get('/tipAccount', async function(req, res){ } //also send notification to the recipient about tipped amount - sendNotification(targetUser, user, 'tip_notification', 'User ' + user + ' has sent you a tip of '+ amount +' AFIT', 'https://actifit.io/'+user); + utils.sendNotification(db, targetUser, user, 'tip_notification', 'User ' + user + ' has sent you a tip of '+ amount +' AFIT', 'https://actifit.io/'+user); //update sending user's token balance & store to db let new_token_count = cur_sender_token_count - amount; @@ -3202,7 +3182,7 @@ app.get('/activateGadget/:user/:gadget/:blockNo/:trxID/:bchain/:benefic?', async gadget_match.benefic = req.params.benefic; //also send notification to the beneficiary about being set for this gadget - sendNotification(req.params.benefic.replace('@',''), user, 'gadget_beneficiary', 'User ' + user + ' has set you as reward beneficiary for one of their gadgets!', 'https://actifit.io/'+user); + utils.sendNotification(db, req.params.benefic.replace('@',''), user, 'gadget_beneficiary', 'User ' + user + ' has set you as reward beneficiary for one of their gadgets!', 'https://actifit.io/'+user); } db.collection('user_gadgets').save(gadget_match); res.send({'status': 'success'}); diff --git a/utils.js b/utils.js index 8fc8394..a4899a6 100644 --- a/utils.js +++ b/utils.js @@ -1321,6 +1321,26 @@ async function verifyFriendTransaction(userA, userB, tx_type, block_num, tx_id, return false; } +async function sendNotification(db, user, action_taker, type, details, url){ + let notification_entry = { + user: user, + action_taker: action_taker, + type: type, + details: details, + url: url, + date: new Date(), + status: 'unread', + }; + try{ + let transaction = await db.collection('notifications').insert(notification_entry); + console.log('success inserting notification data'); + return true; + }catch(err){ + console.log('error'); + return false; + } +} + module.exports = { updateSteemVariables: updateSteemVariables, getVotingPower: getVotingPower, @@ -1361,4 +1381,5 @@ async function verifyFriendTransaction(userA, userB, tx_type, block_num, tx_id, removeArrMatchLodash: removeArrMatchLodash, validateAccountLogin: validateAccountLogin, processSteemTrx: processSteemTrx, + sendNotification: sendNotification, } From 48b872702a61e9e75232316e90d29e5bc6ac8b89 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 1 May 2020 18:07:46 +0300 Subject: [PATCH 161/193] Implement post reward notifications Implement post reward notifications --- curation-bot.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 904e521..43a6841 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -945,9 +945,10 @@ function BuyAndBurn(test){ function setSteemPrice(json){ steem_price = parseFloat(json.steem.usd); console.log('STEEM price:'+steem_price) - if (!config.testing){ + //witness deactivated. No further need to broadcast + /*if (!config.testing){ broadcastFeed('STEEM') - } + }*/ } function setSbdPrice(json){ @@ -2609,6 +2610,9 @@ async function sendVote(post, retries, power_per_vote) { db.collection('exchange_afit_steem').save(cur_upvote_entry); } + //notify user of voting success + utils.sendNotification(db, account.name, post.author, 'post_reward', 'Your post "'+ post.title + '" has been rewarded', 'https://actifit.io/'+post.url); + if(config.comment_location && config.comment){ await sendComment(post, 0, vote_weight, config.active_hive_node) @@ -2891,7 +2895,12 @@ async function sendVote(post, retries, power_per_vote) { } try{ - + + if (post.additional_vote_weight && vote_weight > config.min_vote_weight_decrease){ + //decrease amount by 1% across votes to be able to reward team + vote_weight -= config.extra_vote_weight_decrease;// + } + utils.log('voting with '+account.name+ ' '+utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); steem.api.setOptions({ url: config.active_node , From 3abf0f7030c75c14dd88b786e29ca4a1085404e0 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Tue, 5 May 2020 14:35:30 +0300 Subject: [PATCH 162/193] Switch rank calculation to HP delegation Switch rank calculation to HP delegation --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 27cb2a1..c69de7d 100644 --- a/app.js +++ b/app.js @@ -1464,7 +1464,7 @@ app.get('/topDelegators', async function (req, res) { }); activeDelegationFunc = async function (userName){ - let user = await db.collection('active_delegations').findOne({_id: userName}, {fields : { _id:0} }); + let user = await db.collection('hive_active_delegations').findOne({_id: userName}, {fields : { _id:0} }); console.log(user); return user; } From 4f97bc3e699e9d79ef9d92b3461f8b17db6d9769 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 May 2020 14:23:18 +0300 Subject: [PATCH 163/193] schedule recurring API node restart calls schedule recurring API node restart calls --- app.js | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/app.js b/app.js index c69de7d..c0490a4 100644 --- a/app.js +++ b/app.js @@ -16,6 +16,8 @@ var config = utils.getConfig(); let ObjectId = require('mongodb').ObjectId; +const request = require("request"); + // Connection URL let url = config.mongo_uri; if (config.testing){ @@ -107,6 +109,40 @@ app.get('/', function (req, res) { }); + +//schedule restart intervals due to memory drain down +function restartApiNode() { + request.post( + { + url: 'https://api.heroku.com/apps/' + config.heroku_app_id + '/dynos/' + config.heroku_app_dyno + '/actions/stop', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/vnd.heroku+json; version=3', + 'Authorization': 'Bearer ' + config.heroku_app_token + } + }, + function(error, response, body) { + console.log(response); + console.log(body); + } + ); +} + +if (process.env.BOT_THREAD == 'MAIN'){ + let j = schedule.scheduleJob({hour: 0, minute: 20}, function(){ + restartApiNode(); + }); + let k = schedule.scheduleJob({hour: 6, minute: 20}, function(){ + restartApiNode(); + }); + let l = schedule.scheduleJob({hour: 12, minute: 20}, function(){ + restartApiNode(); + }); + let m = schedule.scheduleJob({hour: 18, minute: 20}, function(){ + restartApiNode(); + }); +} + //initial load let account = null; let accountRefresh = false; @@ -3068,6 +3104,11 @@ app.get("/friendships", async function(req, res){ res.send(gadgets); }); +app.get("/pendingFriendships", async function(req, res){ + let friendRequests = await db.collection('user_requests').find({status: 'pending'}).toArray(); + res.send(friendRequests); +}); + app.get("/userRequests", async function(req, res){ let gadgets = await db.collection('user_requests').find().toArray(); res.send(gadgets); From 671dad2855c4de8e7325188b01eca903498ea0f8 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 13 May 2020 17:10:19 +0300 Subject: [PATCH 164/193] New endpoints for notifications New endpoints for notifications --- app.js | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index c0490a4..49796a9 100644 --- a/app.js +++ b/app.js @@ -1054,9 +1054,25 @@ app.get('/markRead/:notif_id', async function (req, res) { }catch(err){ console.log('error'); res.send({status: 'error'}); - } - + } +}); +/* end point for marking all user's notifications as read */ +app.get('/markAllRead/', async function (req, res) { + if (!req.query || !req.query.user){ + res.send({status: 'error'}); + } + let notif_to_update = { + user: req.query.user, + }; + try{ + let transaction = await db.collection('notifications').update(notif_to_update, { $set: {status: 'read'} } ); + console.log('success updating notification status'); + res.send({status: 'success'}); + }catch(err){ + console.log('error'); + res.send({status: 'error'}); + } }); /* end point for tracking gadget buy orders */ @@ -1362,18 +1378,24 @@ app.get('/dropFriendship/:userA/:userB/:blockNo/:trxID/:bchain', async function } }); -/* end point for fetching all users notifications */ +/* end point for fetching all users unread notifications */ app.get('/activeNotifications/:user', async function (req, res) { let activeNotifications = await db.collection('notifications').find({user: req.params.user, status: 'unread'}).toArray(); res.send(activeNotifications.reverse()); }); -/* end point for fetching all users notifications */ +/* end point for fetching all users read notifications */ app.get('/readNotifications/:user', async function (req, res) { let activeNotifications = await db.collection('notifications').find({user: req.params.user, status: 'read'}).toArray(); res.send(activeNotifications.reverse()); }); +/* end point for fetching all users notifications */ +app.get('/allNotifications/:user', async function (req, res) { + let activeNotifications = await db.collection('notifications').find({user: req.params.user}).toArray(); + res.send(activeNotifications.reverse()); +}); + /* end point for fetching all users badges */ app.get('/allUserBadges/', async function (req, res) { let badges = await db.collection('user_badges').find().toArray(); From 2fdfbaf1413b8983d9b185a911c4ce856ad4898f Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sun, 17 May 2020 01:18:41 +0300 Subject: [PATCH 165/193] Implement token moves from hive-engine Add support to allow moving tokens over from hive-engine to actifit.io wallet --- app.js | 77 +++++++++++++++++++++++++++++++++++++++++++------------- utils.js | 8 +++--- 2 files changed, 65 insertions(+), 20 deletions(-) diff --git a/app.js b/app.js index 49796a9..c60d975 100644 --- a/app.js +++ b/app.js @@ -51,6 +51,8 @@ MongoClient.connect(url, function(err, client) { // Get the documents collection collection = db.collection(collection_name); + //clearCorruptData(); + //disableUserLogin(); } else { @@ -59,6 +61,12 @@ MongoClient.connect(url, function(err, client) { }); +async function clearCorruptData(){ + let res = await db.collection('token_transactions').remove({exchange: 'HE'}); + console.log(res); + console.log('annnd done'); +} + let schedule = require('node-schedule') const SSC = require('sscjs'); @@ -1043,7 +1051,7 @@ app.get('/userFriends/:user', async function (req, res) { }); /* end point for marking a notification as read */ -app.get('/markRead/:notif_id', async function (req, res) { +app.get('/markRead/:notif_id', checkHdrs, async function (req, res) { let notif_to_update = { _id: new ObjectId(req.params.notif_id), }; @@ -1057,17 +1065,32 @@ app.get('/markRead/:notif_id', async function (req, res) { } }); -/* end point for marking all user's notifications as read */ -app.get('/markAllRead/', async function (req, res) { - if (!req.query || !req.query.user){ +/* end point for marking a notification as Unread */ +app.get('/markUnread/:notif_id', checkHdrs, async function (req, res) { + let notif_to_update = { + _id: new ObjectId(req.params.notif_id), + }; + try{ + let transaction = await db.collection('notifications').update(notif_to_update, { $set: {status: 'unread'} } ); + console.log('success updating notification status'); + res.send({status: 'success'}); + }catch(err){ + console.log('error'); res.send({status: 'error'}); } +}); + +/* end point for marking all user's notifications as read */ +app.get('/markAllRead/', checkHdrs, async function (req, res) { let notif_to_update = { user: req.query.user, }; + console.log('markAllRead'); + console.log(notif_to_update); try{ - let transaction = await db.collection('notifications').update(notif_to_update, { $set: {status: 'read'} } ); + let transaction = await db.collection('notifications').update(notif_to_update, { $set: {status: 'read'} }, {multi: true} ); console.log('success updating notification status'); + console.log(transaction); res.send({status: 'success'}); }catch(err){ console.log('error'); @@ -2723,7 +2746,13 @@ storeReferralReward = async function (req){ app.get('/confirmAFITSEBulk', async function(req,res){ //let's call the service by S-E - let url = new URL(config.steem_engine_trans_acct_his_lrg); + + let bchain = (req.query&&req.query.bchain?req.query.bchain:'HIVE'); + + let url = new URL(config.hive_engine_trans_acct_his); + if (bchain == 'STEEM'){ + url = new URL(config.steem_engine_trans_acct_his); + } //console.log(config.steem_engine_trans_acct_his_lrg); //connect with our service to confirm AFIT received to proper wallet try{ @@ -2735,19 +2764,27 @@ app.get('/confirmAFITSEBulk', async function(req,res){ trx_entries.forEach( async function(entry){ console.log(entry); let user = entry.from; - if (user != config.steem_engine_actifit_se){ + if (user != config.steem_engine_actifit_se && user != config.hive_engine_actifit_he){ + + let exchangeType = 'HE'; + + if (bchain == 'STEEM'){ + exchangeType = 'SE'; + } + //query to see if entry already stored let tokenExchangeTransQuery = { user: user, - se_trx_ref: entry.txid + se_trx_ref: entry.transactionId } //store the transaction to the user's profile let tokenExchangeTrans = { user: user, - reward_activity: 'Move AFIT SE to Actifit Wallet', + reward_activity: 'Move AFIT ' + exchangeType + ' to Actifit Wallet', token_count: parseFloat(entry.quantity), - se_trx_ref: entry.txid, - date: new Date(entry.timestamp) + se_trx_ref: entry.transactionId, + exchange: exchangeType, + date: new Date(entry.timestamp * 1000) //timestamp linux convert to seconds first } try{ console.log(tokenExchangeTrans); @@ -2808,25 +2845,31 @@ app.get('/confirmAFITSEReceipt', async function(req,res){ let afit_amount = 0; let found_entry = false; try{ + let bchain = (req.query&&req.query.bchain?req.query.bchain:'HIVE'); //attempt to find matching transaction let targetUser = req.query.user; - let match_trx = await utils.confirmSEAFITReceived(targetUser); + let match_trx = await utils.confirmSEAFITReceived(targetUser, bchain); console.log(match_trx); + let exchangeType = 'HE'; + if (bchain == 'STEEM'){ + exchangeType = 'SE'; + } //we found a match if (match_trx){ - found_entry = true; + found_entry = true; //query to see if entry already stored let tokenExchangeTransQuery = { user: targetUser, - se_trx_ref: match_trx.txid + se_trx_ref: match_trx.transactionId } //store the transaction to the user's profile let tokenExchangeTrans = { user: targetUser, - reward_activity: 'Move AFIT SE to Actifit Wallet', + reward_activity: 'Move AFIT '+ exchangeType + ' to Actifit Wallet', token_count: parseFloat(match_trx.quantity), - se_trx_ref: match_trx.txid, - date: new Date(match_trx.timestamp) + se_trx_ref: match_trx.transactionId, + exchange: exchangeType, + date: new Date(match_trx.timestamp * 1000) //timestamp linux convert to seconds first } try{ console.log(tokenExchangeTrans); diff --git a/utils.js b/utils.js index a4899a6..99a773c 100644 --- a/utils.js +++ b/utils.js @@ -176,7 +176,7 @@ var HOURS = 60 * 60; //function handles confirming if AFIT from SE were received - async function confirmSEAFITReceived (targetUser) { + async function confirmSEAFITReceived (targetUser, bchain) { getConfig(); //track attempts for timeout let attempts = 1; @@ -187,8 +187,10 @@ var HOURS = 60 * 60; attempts += 1; console.log('Check AFIT Power Up'); //let's call the service by S-E - let url = new URL(config.steem_engine_trans_acct_his); - console.log(config.steem_engine_trans_acct_his); + let url = new URL(config.hive_engine_trans_acct_his); + if (bchain == 'STEEM'){ + url = new URL(config.steem_engine_trans_acct_his); + } //connect with our service to confirm AFIT received to proper wallet try{ let se_connector = await fetch(url); From 1a9db82c8f69885a945470ec20daf4c1f9472bb7 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 29 May 2020 12:13:58 +0300 Subject: [PATCH 166/193] Implement direct HE-SE token move Implement direct HE-SE token move Powerdown to HE instead of SE --- app.js | 164 +++++++++++++++++++++++++++++++++++++++++++++++-- delegations.js | 10 +-- utils.js | 92 +++++++++++++++++++++++++++ 3 files changed, 257 insertions(+), 9 deletions(-) diff --git a/app.js b/app.js index c60d975..4c4bb7a 100644 --- a/app.js +++ b/app.js @@ -1644,7 +1644,7 @@ app.get('/is_banned/:user', async function (req, res) { /* end point for returning if a user is powering down AFIT*/ app.get('/isPoweringDown/:user', async function (req, res) { - let poweringDown = await db.collection('powering_down').findOne({user: req.params.user}); + let poweringDown = await db.collection('powering_down_he').findOne({user: req.params.user}); console.log (poweringDown) if (!poweringDown){ res.send({}); @@ -1655,7 +1655,7 @@ app.get('/isPoweringDown/:user', async function (req, res) { /* end point for returning the list of users powering down AFIT*/ app.get('/poweringDownList/', async function (req, res) { - let poweringDown = await db.collection('powering_down').find().toArray(); + let poweringDown = await db.collection('powering_down_he').find().toArray(); console.log (poweringDown) res.send(poweringDown); }); @@ -1692,7 +1692,7 @@ app.get('/cancelAFITMoveSE', async function(req, res){ //reached here, we're fine try{ - let result = await db.collection('powering_down').remove({user: req.query.user}); + let result = await db.collection('powering_down_he').remove({user: req.query.user}); res.send({'status': 'Success'}); }catch(err){ console.log(err); @@ -1801,7 +1801,7 @@ app.get('/initiateAFITMoveSE', async function(req, res){ try{ console.log(tokenPowerDownTrans); - let transaction = await db.collection('powering_down').update(tokenPowerDownQuery, tokenPowerDownTrans, { upsert: true }); + let transaction = await db.collection('powering_down_he').update(tokenPowerDownQuery, tokenPowerDownTrans, { upsert: true }); console.log('success inserting power down data'); }catch(err){ console.log(err); @@ -2928,6 +2928,162 @@ app.get('/confirmAFITSEReceipt', async function(req,res){ }); +//function handles the process of confirming AFITX S-E receipt into proper account, and then duplicating to new exchange +app.get('/proceedAfitxTransition', async function(req,res){ + if (!req.query.user || !req.query.amount || !req.query.txid){ + res.send('{}'); + }else{ + //keeping request alive to avoid timeouts + let intID = setInterval(function(){ + res.write(' '); + }, 6000); + let found_entry = false; + let afitx_amount = ''; + let status = ''; + try{ + let bchain = (req.query&&req.query.bchain?req.query.bchain:'HIVE'); + //attempt to find matching transaction + let targetUser = req.query.user; + let amount = req.query.amount; + let txid = req.query.txid; + let match_trx = await utils.confirmAFITXTransition(targetUser, txid, amount, bchain); + console.log(match_trx); + + //we found a match + if (match_trx){ + found_entry = true; + //query to see if entry already stored + let tokenExchangeTransQuery = { + user: targetUser, + token_count: amount, + trx: match_trx.transactionId, + block: match_trx.blockNumber, + chain: bchain, + } + //store the transaction to the user's profile + let tokenExchangeTrans = { + user: targetUser, + action: 'Move AFITX ', + token_count: amount, + net_amount: amount * (1-config.trx_burn_rate),//apply 0.5% burn rate + trx: match_trx.transactionId, + block: match_trx.blockNumber, + chain: bchain, + date: new Date(match_trx.timestamp * 1000) //timestamp linux convert to seconds first + } + console.log(tokenExchangeTrans); + try{ + //insert the query ensuring we do not write it twice + let transaction = await db.collection('afitx_transitions').find(tokenExchangeTransQuery).toArray(); + if (Array.isArray(transaction) && transaction.length > 0){ + //match found, duplicate request, ignore + console.log('Existing processed transaction. Ignore'); + status = 'error'; + }else{ + //apply 0.5% burn rate + amount = amount * (1-config.trx_burn_rate) + let res = await utils.proceedAfitxMove(targetUser, amount, (bchain=='STEEM'?'HIVE':'STEEM')); + let transaction = await db.collection('afitx_transitions').insert(tokenExchangeTrans); + console.log('success moving & inserting transaction data'); + afitx_amount = amount; + status = 'success'; + } + }catch(err){ + console.log(err); + res.write(JSON.stringify({'error': 'Error moving AFITX tokens to user balance'})); + res.end(); + return; + } + } + }catch(err){ + console.log(err); + } + //we're done, let's clear our running interval + clearInterval(intID); + //send response + res.write(JSON.stringify({'afitx_transition': status, 'afitx_amount': afitx_amount})); + res.end(); + } +}); + +//function handles the process of confirming AFITX S-E receipt into proper account, and then duplicating to new exchange +app.get('/proceedAfitTransition', async function(req,res){ + if (!req.query.user || !req.query.amount || !req.query.txid){ + res.send('{}'); + }else{ + //keeping request alive to avoid timeouts + let intID = setInterval(function(){ + res.write(' '); + }, 6000); + let found_entry = false; + let afitx_amount = ''; + let status = ''; + try{ + let bchain = (req.query&&req.query.bchain?req.query.bchain:'HIVE'); + //attempt to find matching transaction + let targetUser = req.query.user; + let amount = req.query.amount; + let txid = req.query.txid; + let standardAfit = 1; + let match_trx = await utils.confirmAFITXTransition(targetUser, txid, amount, bchain, standardAfit); + console.log(match_trx); + + //we found a match + if (match_trx){ + found_entry = true; + //query to see if entry already stored + let tokenExchangeTransQuery = { + user: targetUser, + token_count: amount, + trx: match_trx.transactionId, + block: match_trx.blockNumber, + chain: bchain, + } + //store the transaction to the user's profile + let tokenExchangeTrans = { + user: targetUser, + action: 'Move AFIT', + token_count: amount, + net_amount: amount * (1-config.trx_burn_rate),//apply 0.5% burn rate + trx: match_trx.transactionId, + block: match_trx.blockNumber, + chain: bchain, + date: new Date(match_trx.timestamp * 1000) //timestamp linux convert to seconds first + } + console.log(tokenExchangeTrans); + try{ + //insert the query ensuring we do not write it twice + let transaction = await db.collection('afit_transitions').find(tokenExchangeTransQuery).toArray(); + if (Array.isArray(transaction) && transaction.length > 0){ + //match found, duplicate request, ignore + console.log('Existing processed transaction. Ignore'); + status = 'error'; + }else{ + //apply 0.5% burn rate + amount = amount * (1-config.trx_burn_rate) + let res = await utils.proceedAfitxMove(targetUser, amount, (bchain=='STEEM'?'HIVE':'STEEM'), standardAfit); + let transaction = await db.collection('afit_transitions').insert(tokenExchangeTrans); + console.log('success moving & inserting transaction data'); + afitx_amount = amount; + status = 'success'; + } + }catch(err){ + console.log(err); + res.write(JSON.stringify({'error': 'Error moving AFIT tokens to user balance'})); + res.end(); + return; + } + } + }catch(err){ + console.log(err); + } + //we're done, let's clear our running interval + clearInterval(intID); + //send response + res.write(JSON.stringify({'afit_transition': status, 'afit_amount': afitx_amount})); + res.end(); + } +}); /* function handles the processing of a buy order */ app.get('/processBuyOrder', async function(req, res){ diff --git a/delegations.js b/delegations.js index 6db05df..3a228c1 100644 --- a/delegations.js +++ b/delegations.js @@ -193,7 +193,7 @@ async function moveAFITToSE(testMode){ db = dbClient.db(dbName) // Get the documents collection - let poweringDown = await db.collection('powering_down').find().toArray(); + let poweringDown = await db.collection('powering_down_he').find().toArray(); console.log (poweringDown) //sign key properly to function with dsteem requirement @@ -284,9 +284,9 @@ async function moveAFITToSE(testMode){ //perform transaction, decrease sender amount let moveTrans = { user: entry.user, - reward_activity: 'Move AFIT to S-E', + reward_activity: 'Move AFIT to H-E', token_count: -amount, - note: 'User Automated transfer of ' + entry.daily_afit_transfer + ' AFIT to S-E', + note: 'User Automated transfer of ' + entry.daily_afit_transfer + ' AFIT to H-E', date: new Date(), } @@ -311,10 +311,10 @@ async function moveAFITToSE(testMode){ //broadcast to BC console.log('broadcast to BC'); if (!testMode){ - client.broadcast.json({ + hiveClient.broadcast.json({ required_auths: [config.account], required_posting_auths: [], - id: 'ssc-mainnet1', + id: 'ssc-mainnet-hive',//ssc-mainnet1 json: JSON.stringify(json_data), }, privateKey).then( result => { diff --git a/utils.js b/utils.js index 99a773c..69661f9 100644 --- a/utils.js +++ b/utils.js @@ -174,6 +174,96 @@ var HOURS = 60 * 60; //}); } + //function handles confirming if AFIT from SE were received + async function confirmAFITXTransition (targetUser, txid, amount, bchain, standardAfit) { + getConfig(); + //track attempts for timeout + let attempts = 1; + let max_attempts = 15; + return new Promise((resolve, reject) => { + th_id = setInterval(async function(){ + if (attempts < max_attempts){ + attempts += 1; + console.log('Check Move'); + //let's call the service by S-E + let url = new URL(config.hive_engine_afitx_trx); + if (standardAfit == 1){ + url = new URL(config.hive_engine_afit_trx); + } + if (bchain == 'STEEM'){ + url = new URL(config.steem_engine_afitx_trx); + if (standardAfit == 1){ + url = new URL(config.steem_engine_afit_trx); + } + } + //connect with our service to confirm AFIT received to proper wallet + try{ + let se_connector = await fetch(url); + let trx_entries = await se_connector.json(); + console.log(trx_entries); + let match_trx; + + //check if we have a proper entry matching user transfer + if (match_trx = trx_entries.find(trx => (trx.from == targetUser && trx.quantity == amount && trx.transactionId == txid))) { + //found match, let's make sure transaction is recent enough + console.log('found match'); + paymentFound = true; + if (paymentFound){ + //need to look again + console.log('found'); + clearInterval(th_id); + resolve(match_trx); + } + } + }catch(err){ + console.log(err); + } + }else{ + //return error + resolve(null); + } + }, 5000); + }); + } + + async function proceedAfitxMove (targetAcct, amount, chain, standardAfit){ + + let transId = 'ssc-mainnet-hive'; + //let targetBchain = 'STEEM'; + //other option is moving tokens from H-E to S-E + if (chain == 'STEEM'){ + //if (this.cur_bchain == 'STEEM'){ + transId = 'ssc-mainnet1'; + //targetBchain = 'HIVE'; + } + let tokenSymbol = 'AFITX'; + if (standardAfit == 1){ + tokenSymbol = 'AFIT'; + } + + let json_data = { + contractName: 'tokens', + contractAction: 'transfer', + contractPayload: { + symbol: tokenSymbol, + to: targetAcct, + quantity: '' + amount,//needs to be string + memo: '' + } + } + + //send out transaction to blockchain + await setProperNode(chain); + let tx = await steem.broadcast.customJsonAsync( + config.active_key, + [ config.account ] , + [], + transId, + JSON.stringify(json_data) + ).catch(err => { + console.log(err.message); + }); + } //function handles confirming if AFIT from SE were received async function confirmSEAFITReceived (targetUser, bchain) { @@ -1384,4 +1474,6 @@ async function sendNotification(db, user, action_taker, type, details, url){ validateAccountLogin: validateAccountLogin, processSteemTrx: processSteemTrx, sendNotification: sendNotification, + confirmAFITXTransition: confirmAFITXTransition, + proceedAfitxMove: proceedAfitxMove } From 781abe37f23d68fea6278b6a40bb17a8240c9b0a Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 29 May 2020 16:30:29 +0300 Subject: [PATCH 167/193] Adjust AFITX Holders to include HE Adjust AFITX Holders query to include HE holdings in addition to SE holdings --- app.js | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 4c4bb7a..5cb22b0 100644 --- a/app.js +++ b/app.js @@ -72,9 +72,12 @@ let schedule = require('node-schedule') const SSC = require('sscjs'); const ssc = new SSC(config.steem_engine_rpc); +const hsc = new SSC(config.hive_engine_rpc); + let rule = new schedule.RecurrenceRule(); let usersAFITXBal = []; +let usersAFITXBalHE = []; let fullSortedAFITXList = []; //initial fetch fetchAFITXBal(0); @@ -207,11 +210,14 @@ async function fetchAFITXBal(offset){ }, 1000); }else{ //if we were not able to fetch entries, we need to try API again - if (offset == 0){ + if (offset == 0 && tempArr.length < 1){ console.log('no AFITX data, fetch again in 30 secs'); setTimeout(function(){ fetchAFITXBal(0); }, 30000); + }else{ + //done with AFITX SE, proceed with AFITX HE + fetchAFITXBalHE(0); } } }catch(err){ @@ -226,6 +232,66 @@ async function fetchAFITXBal(offset){ //console.log(usersAFITXBal); } +async function fetchAFITXBalHE(offset){ + try{ + console.log('--- Fetch new AFITX token balance ---'); + console.log(offset); + let tempArr = await hsc.find('tokens', 'balances', { symbol : 'AFITX' }, 1000, offset, '', false) //max amount, offset, + if (offset == 0 && tempArr.length > 0){ + console.log('>>Found new results, reset older ones'); + //reset existing data if we have fresh new data + usersAFITXBalHE = []; + } + usersAFITXBalHE = usersAFITXBalHE.concat(tempArr); + + if (tempArr.length > 999){ + //we possibly have more entries, let's call again + setTimeout(function(){ + fetchAFITXBalHE(usersAFITXBalHE.length); + }, 1000); + }else{ + //if we were not able to fetch entries, we need to try API again + if (offset == 0 && tempArr.length < 1){ + console.log('no AFITX data, fetch again in 30 secs'); + setTimeout(function(){ + fetchAFITXBalHE(0); + }, 30000); + }else{ + //done, let's merge both SE & HE lists + for (let i=0;i entry.account === usersAFITXBal[i].account); + if (match){ + usersAFITXBal[i].sebalance = usersAFITXBal[i].balance; + usersAFITXBal[i].hebalance = match.balance; + usersAFITXBal[i].balance = parseFloat(usersAFITXBal[i].balance) + parseFloat(match.balance); + usersAFITXBal[i].heholder = true; + } + } + //append HE holdings + for (let i=0;i entry.account === usersAFITXBalHE[i].account); + if (!match){ + usersAFITXBal.push(usersAFITXBalHE[i]); + //usersAFITXBal[i].hebalance = match.balance; + //usersAFITXBal[i].balance = parseFloat(usersAFITXBal[i].balance) + parseFloat(match.balance); + } + } + } + } + }catch(err){ + console.log(err); + if (offset == 0){ + console.log('no AFITX data, fetch again in 30 secs'); + setTimeout(function(){ + fetchAFITXBalHE(0); + }, 30000); + } + } + //console.log(usersAFITXBal); +} + async function getAFITXUserData(user){ let ind = fullSortedAFITXList.findIndex(v => v.account == user) let entry = fullSortedAFITXList.find(v => v.account == user) From 4e1894b7389f4610ef432f323f89be465ddd1ae6 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 30 May 2020 20:17:21 +0300 Subject: [PATCH 168/193] Implement proper AFITX cross exch check Implement proper AFITX cross exchange checks to ensure total amount visibility for power down --- app.js | 21 ++++++++++++++++----- delegations.js | 16 +++++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/app.js b/app.js index 5cb22b0..e75ad0f 100644 --- a/app.js +++ b/app.js @@ -1828,26 +1828,37 @@ app.get('/initiateAFITMoveSE', async function(req, res){ res.send({'error': 'Account does not have enough AFIT funds'}); return; } + let tot_afitx_bal = 0; let afitx_se_balance = 0; + let afitx_he_balance = 0; //confirm amount within AFITX conditions let bal = await ssc.findOne('tokens', 'balances', { account: user, symbol: 'AFITX' }); + let bal_he = await hsc.findOne('tokens', 'balances', { account: user, symbol: 'AFITX' }); + if (bal){ - afitx_se_balance = bal.balance; + afitx_se_balance = parseFloat(bal.balance); + } + if (bal_he){ + afitx_he_balance = parseFloat(bal_he.balance); + } + tot_afitx_bal = afitx_se_balance + afitx_he_balance; + /*if (bal || bal_he){ + }else{ res.send({'error': 'Unable to fetch AFITX Funds. Try again later.'}); return; - } + }*/ //make sure user has at least 0.1 AFITX to move tokens - if (afitx_se_balance < 0.1){ + if (tot_afitx_bal < 0.1){ res.send({'error': 'You do not have enough AFITX to move AFIT tokens over.'}); return; } //console.log(amount_to_powerdown); //console.log(this.afitx_se_balance); //calculate amount that can be transferred daily - if (amount / 100 > afitx_se_balance){ - res.send({'error': 'You do not have enough AFITX to move '+afitx_se_balance+ ' AFIT'}); + if (amount / 100 > tot_afitx_bal){ + res.send({'error': 'You do not have enough AFITX to move '+amount+ ' AFIT'}); return; } diff --git a/delegations.js b/delegations.js index 3a228c1..e56676f 100644 --- a/delegations.js +++ b/delegations.js @@ -223,7 +223,9 @@ async function moveAFITToSE(testMode){ //let's make sure user still has proper AFITX amount let userHasProperFunds = true; + let afitx_tot_bal = 0; let afitx_se_balance = 0; + let afitx_he_balance = 0; let bal = await ssc.findOne('tokens', 'balances', { account: entry.user, symbol: 'AFITX' }); if (bal){ afitx_se_balance = bal.balance; @@ -231,14 +233,22 @@ async function moveAFITToSE(testMode){ console.log('error - Unable to fetch AFITX Funds. Try again later.'); return; } + bal = await hsc.findOne('tokens', 'balances', { account: entry.user, symbol: 'AFITX' }); + if (bal){ + afitx_he_balance = bal.balance; + }else{ + console.log('error - Unable to fetch AFITX Funds. Try again later.'); + return; + } + afitx_tot_bal = parseFloat(afitx_se_balance) + parseFloat(afitx_he_balance); //make sure user has at least 0.1 AFITX to move tokens - if (afitx_se_balance < 0.1){ + if (afitx_tot_bal < 0.1){ userHasProperFunds = false; } //console.log(amount_to_powerdown); //console.log(this.afitx_se_balance); //calculate amount that can be transferred daily - if (parseFloat(entry.daily_afit_transfer) / 100 > afitx_se_balance){ + if (parseFloat(entry.daily_afit_transfer) / 100 > afitx_tot_bal){ userHasProperFunds = false; } @@ -266,7 +276,7 @@ async function moveAFITToSE(testMode){ userHasProperFunds = false; } - console.log('entry.user:'+entry.user+ ' afit bal:' + cur_user_token_count + ' bal:'+afitx_se_balance+' userHasProperFunds:'+userHasProperFunds); + console.log('entry.user:'+entry.user+ ' afit bal:' + cur_user_token_count + ' bal:'+afitx_tot_bal+' userHasProperFunds:'+userHasProperFunds); if (userHasProperFunds){ setTimeout(async function(){ From bf0afe9dadd979a3ef72ee957f522009f822ddef Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 3 Jun 2020 14:03:32 +0300 Subject: [PATCH 169/193] Fix issue with sending our daily AFIT to HE Fix issue with sending our daily AFIT to HE --- delegations.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/delegations.js b/delegations.js index e56676f..d68f883 100644 --- a/delegations.js +++ b/delegations.js @@ -73,6 +73,8 @@ if (process.env.BOT_THREAD == 'MAIN'){ const SSC = require('sscjs'); const ssc = new SSC(config.steem_engine_rpc); +const hsc = new SSC(config.hive_engine_rpc); + //airdropAFITX(); //moveAFITToSE(true); @@ -230,15 +232,15 @@ async function moveAFITToSE(testMode){ if (bal){ afitx_se_balance = bal.balance; }else{ - console.log('error - Unable to fetch AFITX Funds. Try again later.'); - return; + console.log('error - Unable to fetch S-E AFITX Funds for '+entry.user+' or funds are zero.'); + //return; } bal = await hsc.findOne('tokens', 'balances', { account: entry.user, symbol: 'AFITX' }); if (bal){ afitx_he_balance = bal.balance; }else{ - console.log('error - Unable to fetch AFITX Funds. Try again later.'); - return; + console.log('error - Unable to fetch H-E AFITX Funds for '+entry.user+' or funds are zero.'); + //return; } afitx_tot_bal = parseFloat(afitx_se_balance) + parseFloat(afitx_he_balance); //make sure user has at least 0.1 AFITX to move tokens From ff48776b64d184e6db93d12d76f2562def4c2aef Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 5 Jun 2020 18:12:31 +0300 Subject: [PATCH 170/193] Fix delegation reward dispatch issue Fix issue with error on missing collection while dropping causing delegation rewards not to process through --- delegations.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/delegations.js b/delegations.js index d68f883..28f2b0e 100644 --- a/delegations.js +++ b/delegations.js @@ -1180,8 +1180,17 @@ async function updateActiveDelegations (delgTrxCol, targetCol) { { $match: { 'steem_power': { '$gt': 0 } } } ] ) + console.log('collections'); + console.log(delgTrxCol); + console.log(targetCol); let activeDelegations = await query.toArray() - await db.collection(targetCol).drop() + console.log('activeDelegations fetched'); + try{ + await db.collection(targetCol).drop() + }catch(err){ + console.log(err); + } + console.log('activeDelegations dropped'); await db.collection(targetCol).insert(activeDelegations) console.log('done updating delegations '+targetCol); return ; From c3ca5236a40abdc7a72ac80478d09791dd5b2099 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 11 Jun 2020 01:33:49 +0300 Subject: [PATCH 171/193] switch hive support to hive-js & dhive switch hive support to hive-js & dhive for voting scripts --- curation-bot.js | 249 +++++++++++++++++++++--------------------------- 1 file changed, 111 insertions(+), 138 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 43a6841..59e9d81 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1,7 +1,7 @@ var fs = require("fs"); const steem = require('steem'); -//const hive = require('steem'); +const hive = require('@hiveio/hive-js'); var utils = require('./utils'); @@ -30,8 +30,6 @@ var skip = false; var version = '0.3.4'; var lucky_winner_id = -1; - -let usersAFITXBal = []; let topUsersAFITX = []; let gadgetsFetched = false; @@ -55,6 +53,17 @@ loadHivePrices(); //kick off loading steem prices in 30 seconds setTimeout(loadSteemPrices, 30*1000); +//set proper nodes +steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true +}); + +hive.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true +}); + var STEEMIT_100_PERCENT = 10000; var STEEMIT_VOTE_REGENERATION_SECONDS = (5 * 60 * 60 * 24); var HOURS = 60 * 60; @@ -75,13 +84,21 @@ setInterval(function() { }); //let's also run the cleanup for any missed AFIT SE to Actifit wallet processes - request('https://actifitbot.herokuapp.com/confirmAFITSEBulk', function (error, response, body) { + request('https://actifitbot.herokuapp.com/confirmAFITSEBulk?bchain=STEEM', function (error, response, body) { console.log('process any missed AFIT SE to Actifit Wallet'); console.log(response); //console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received //console.log('error: '+ error) }); + //let's also run the cleanup for any missed AFIT SE to Actifit wallet processes + request('https://actifitbot.herokuapp.com/confirmAFITSEBulk?bchain=HIVE', function (error, response, body) { + console.log('process any missed AFIT HE to Actifit Wallet'); + console.log(response); + //console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received + //console.log('error: '+ error) + }); + //also load prices & broadcast updates to witness nodes (STEEM & HIVE) loadHivePrices(); //in 30 seconds load steem prices @@ -186,10 +203,11 @@ let tokensBurntLastRound = false; const SSC = require('sscjs'); const ssc = new SSC(config.steem_engine_rpc); +const hsc = new SSC(config.hive_engine_rpc); // Initial Load of top AFITX token holders // Top 25 will be stored in topUsersAFITX -fetchAFITXBal(0); +fetchAFITXTopHolders(); //grab list of active gadgets async function fetchGadgets(){ @@ -218,17 +236,14 @@ setInterval(function(){ if (!is_voting){ // Load updated top AFITX token holders every 10 minutes // Top 25 will be stored in topUsersAFITX - fetchAFITXBal(0); + fetchAFITXTopHolders(); } }catch(err){ console.log(err); } }, 1200000); // every 20 minutes (1200000) -steem.api.setOptions({ - url: config.active_hive_node , - //useAppbaseApi: true -}); + utils.log("* START - Version: " + version + " *"); @@ -301,6 +316,7 @@ MongoClient.connect(url, function(err, client) { let test = await db.collection('user_gadgets').findOne({ benefic: {$in: ['silvertop', '@silvertop']}, status: "active", gadget: gadgetId, }) console.log(test);*/ + //testBoostData(); //only start the process once we connected to the DB startProcess(); @@ -678,10 +694,7 @@ async function startProcess() { //load steem account data - await steem.api.setOptions({ - url: config.active_node , - //useAppbaseApi: true - }); + await steem.api.getAccounts([config.account], function (err, result) { if (err || !result) @@ -694,13 +707,13 @@ async function startProcess() { - await steem.api.setOptions({ + /*await steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); + });*/ - // Load the bot account info - steem.api.getAccounts([config.account], function (err, result) { + // Load hive account info + hive.api.getAccounts([config.account], function (err, result) { if (err || !result) utils.log(err, result); else { @@ -809,10 +822,10 @@ async function startProcess() { // Load Steem global variables - properties = await steem.api.getDynamicGlobalPropertiesAsync(); + properties = await hive.api.getDynamicGlobalPropertiesAsync(); //grab reward fund data - rewardFund = await steem.api.getRewardFundAsync("post"); - rewardBalance = parseFloat(rewardFund.reward_balance.replace(" STEEM", "")); + rewardFund = await hive.api.getRewardFundAsync("post"); + rewardBalance = parseFloat(rewardFund.reward_balance.replace(" STEEM", "").replace(" HIVE", "")); recentClaims = rewardFund.recent_claims; totalSteem = Number(properties.total_vesting_fund_steem.split(' ')[0]); @@ -1064,19 +1077,19 @@ function broadcastFeed (type) { //below two lines are hacks since steem-js is not yet accepting HIVE and HBD //pegged_cur = ' HBD'; type = 'STEEM'; - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); + });*/ }else{ - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true - }); + });*/ } let exchange_rate = { base: price_val.toFixed(3) + pegged_cur, quote: (1 / peg_multi).toFixed(3) + ' ' + type }; utils.log('Broadcasting ' + origType + ' feed_publish transaction: ' + JSON.stringify(exchange_rate)); - steem.broadcast.feedPublish(config.active_key, config.account, exchange_rate, function (err, result) { + hive.broadcast.feedPublish(config.active_key, config.account, exchange_rate, function (err, result) { if (result && !err) { console.log(result); utils.log('Broadcast successful!'); @@ -1103,13 +1116,13 @@ function processVotes(query, subsequent) { //top 25 will be stored in topUsersAFITX //fetchAFITXBal(0); - + /* steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true -}); +});*/ - steem.api.getDiscussionsByCreated(query, async function (err, result) { + hive.api.getDiscussionsByCreated(query, async function (err, result) { //track how many queries were ran queryCount += 1; if (result && !err) { @@ -1550,7 +1563,7 @@ function processVotes(query, subsequent) { /******************* comments criteria *********************/ var matching_comment_count = 0; //if (!config.testing){ - let comments = await steem.api.getContentRepliesAsync(post.author, post.permlink); + let comments = await hive.api.getContentRepliesAsync(post.author, post.permlink); for(var cmt_it = 0; cmt_it < comments.length; cmt_it++) { //utils.log('>>>>>>'+comments[cmt_it].body); @@ -1960,9 +1973,9 @@ function processVotes(query, subsequent) { //calculate current vote value, and relating voting percentage needed //get vote value at 100% - let full_vote_value = getVoteValueUSD(100, account, 100, sbd_price); + let full_vote_value = getVoteValueUSD(100, account, 100, hive_price); console.log('full_vote_value') - //console.log(full_vote_value) + console.log(full_vote_value) console.log('topUsersAFITX') //console.log(topUsersAFITX); @@ -1995,16 +2008,16 @@ function processVotes(query, subsequent) { matched_exchanges += 1; //found a match, need to increase rewards according to AFIT pay //calculate total paid AFIT in USD (which should be equal to a 65% reward, since Actifit removes 10% benefic, and author reward removes 75% - let usd_val_no_benef = parseFloat(cur_upvote_entry.paid_afit) * parseFloat(cur_afit_price.unit_price_usd); + let usd_val_no_benef = parseFloat(cur_upvote_entry.paid_afit) * parseFloat(cur_afit_price.unit_price_usd);//20*0.02=0.4 //expand the USD val to take into consideration 75% curation reward - let usd_val_no_curation = usd_val_no_benef * 0.75 / 0.65 + let usd_val_no_curation = usd_val_no_benef * 0.75 / 0.65; //0.4*0.75/0.65=0.4615384615384615 //final upvote value after avoiding deductions - let usd_val = usd_val_no_benef / 0.5 + let usd_val = usd_val_no_benef / 0.5; //0.4/0.5=0.8 //emulate proper voting power to give user matching rewards - let user_added_vote_weight = usd_val * 100 / full_vote_value; + let user_added_vote_weight = usd_val * 100 / full_vote_value; //0.8*100/full_vote_value let entry_index = votePosts.findIndex( user_post => user_post.author === cur_upvote_entry.user); @@ -2141,49 +2154,12 @@ function processVotes(query, subsequent) { }); } -async function fetchAFITXBal(offset){ - try{ - console.log('--- Fetch AFITX token balance --- '+offset); - console.log(offset); - let tempArr = await ssc.find('tokens', 'balances', { symbol : 'AFITX' }, 1000, offset, '', false) //max amount, offset, - if (offset == 0 && tempArr.length > 0){ - console.log('>>Found new results, reset older ones'); - //reset existing data if we have fresh new data - usersAFITXBal = []; - topUsersAFITX = []; - } - usersAFITXBal = usersAFITXBal.concat(tempArr); - if (tempArr.length > 999){ - //we possibly have more entries, let's call again - setTimeout(function(){ - fetchAFITXBal(usersAFITXBal.length); - }, 1000); - }else{ - //if we were not able to fetch entries, we need to try API again - if (offset == 0){ - console.log('no AFITX data, fetch again in 30 secs'); - setTimeout(function(){ - fetchAFITXBal(0); - }, 30000); - }else{ - console.log('AFITX list fetching complete'); - usersAFITXBal = utils.sortArrLodash(usersAFITXBal); - //skip first entry as thats Actifit account - topUsersAFITX = usersAFITXBal.slice(1, config.topAFITXCount); - - //console.log(topUsersAFITX); - } - } - }catch(err){ - console.log(err); - if (offset == 0){ - console.log('no AFITX data, fetch again in 30 secs'); - setTimeout(function(){ - fetchAFITXBal(0); - }, 30000); - } - } - //console.log(usersAFITXBal); +async function fetchAFITXTopHolders(){ + + console.log('--- Fetch AFITX top users --- '); + let holderList = await fetch('https://actifitbot.herokuapp.com/topAFITXHolders?count='+config.topAFITXCount); + topUsersAFITX = await holderList.json(); + console.log(topUsersAFITX); } var post_rank = 0; @@ -2370,21 +2346,22 @@ async function sendVote(post, retries, power_per_vote) { //first bchain transactions console.log('set HIVE node'); + /* steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); + });*/ //vote first using pay and funds accounts only if we have an AFIT/STEEM exchange operation and we have room to upvote using helping accounts if (post.additional_vote_weight && post.helperVotes){ let vote_percent_add_accounts = config.helping_account_percent;//at 50%: 5000 try{ utils.log('voting with '+config.full_pay_benef_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); - res = await steem.broadcast.sendAsync( + });*/ + res = await hive.broadcast.sendAsync( { operations: [ ['vote', @@ -2411,11 +2388,11 @@ async function sendVote(post, retries, power_per_vote) { try{ utils.log('voting with '+config.pay_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); - res = await steem.broadcast.sendAsync( + });*/ + res = await hive.broadcast.sendAsync( { operations: [ ['vote', @@ -2446,11 +2423,11 @@ async function sendVote(post, retries, power_per_vote) { try{ if (config.zzan_active){ utils.log('voting with '+config.zzan_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); - res = await steem.broadcast.sendAsync( + });*/ + res = await hive.broadcast.sendAsync( { operations: [ ['vote', @@ -2479,11 +2456,11 @@ async function sendVote(post, retries, power_per_vote) { try{ if (config.sports_active){ utils.log('voting with '+config.sports_active+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); - res = await steem.broadcast.sendAsync( + });*/ + res = await hive.broadcast.sendAsync( { operations: [ ['vote', @@ -2515,11 +2492,11 @@ async function sendVote(post, retries, power_per_vote) { try{ utils.log('voting with '+config.appics_account+ ' '+utils.format(post.boost_apx_percent / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ - url: config.active_hive_node , - //useAppbaseApi: true - }); - res = await steem.broadcast.sendAsync( + /*steem.api.setOptions({ + url: config.active_hive_node , + //useAppbaseApi: true + });*/ + res = await hive.broadcast.sendAsync( { operations: [ ['vote', @@ -2547,11 +2524,11 @@ async function sendVote(post, retries, power_per_vote) { try{ utils.log('voting with '+config.rewards_account+ ' '+utils.format(net_rewards_vote_weight / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); - res = await steem.broadcast.sendAsync( + });*/ + res = await hive.broadcast.sendAsync( { operations: [ ['vote', @@ -2578,11 +2555,11 @@ async function sendVote(post, retries, power_per_vote) { try{ utils.log('voting with '+account.name+ ' '+utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_hive_node , //useAppbaseApi: true - }); - res = await steem.broadcast.sendAsync( + });*/ + res = await hive.broadcast.sendAsync( { operations: [ ['vote', @@ -2611,7 +2588,7 @@ async function sendVote(post, retries, power_per_vote) { } //notify user of voting success - utils.sendNotification(db, account.name, post.author, 'post_reward', 'Your post "'+ post.title + '" has been rewarded', 'https://actifit.io/'+post.url); + utils.sendNotification(db, post.author, account.name, 'post_reward', 'Your activity report "'+ post.title + '" has been rewarded!', 'https://actifit.io'+post.url); if(config.comment_location && config.comment){ @@ -2687,10 +2664,10 @@ async function sendVote(post, retries, power_per_vote) { //second blockchain transactions console.log('set STEEM node'); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true - }); + });*/ //vote first using pay and funds accounts only if we have an AFIT/STEEM exchange operation and we have room to upvote using helping accounts if (post.additional_vote_weight && post.helperVotes){ let vote_percent_add_accounts = config.helping_account_percent;//at 50%: 5000 @@ -2698,10 +2675,10 @@ async function sendVote(post, retries, power_per_vote) { utils.log('voting with '+config.full_pay_benef_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true - }); + });*/ res = await steem.broadcast.sendAsync( { operations: [ @@ -2729,10 +2706,10 @@ async function sendVote(post, retries, power_per_vote) { try{ utils.log('voting with '+config.pay_account+ ' '+utils.format(vote_percent_add_accounts / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true - }); + });*/ res = await steem.broadcast.sendAsync( { operations: [ @@ -2764,10 +2741,10 @@ async function sendVote(post, retries, power_per_vote) { try{ if (config.zzan_active){ utils.log('voting with '+config.zzan_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true - }); + });*/ res = await steem.broadcast.sendAsync( { operations: [ @@ -2797,10 +2774,10 @@ async function sendVote(post, retries, power_per_vote) { try{ if (config.sports_active){ utils.log('voting with '+config.sports_account+ ' '+utils.format(stdrd_vote_weight / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true - }); + });*/ res = await steem.broadcast.sendAsync( { operations: [ @@ -2833,10 +2810,10 @@ async function sendVote(post, retries, power_per_vote) { try{ utils.log('voting with '+config.appics_account+ ' '+utils.format(post.boost_apx_percent / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ - url: config.active_node , - //useAppbaseApi: true - }); + /*steem.api.setOptions({ + url: config.active_node , + //useAppbaseApi: true + });*/ res = await steem.broadcast.sendAsync( { operations: [ @@ -2865,10 +2842,10 @@ async function sendVote(post, retries, power_per_vote) { try{ utils.log('voting with '+config.rewards_account+ ' '+utils.format(net_rewards_vote_weight / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true - }); + });*/ res = await steem.broadcast.sendAsync( { operations: [ @@ -2902,10 +2879,10 @@ async function sendVote(post, retries, power_per_vote) { } utils.log('voting with '+account.name+ ' '+utils.format(vote_weight / 100) + '% vote cast for: ' + post.url); - steem.api.setOptions({ + /*steem.api.setOptions({ url: config.active_node , //useAppbaseApi: true - }); + });*/ res = await steem.broadcast.sendAsync( { operations: [ @@ -3147,10 +3124,14 @@ async function sendComment(post, retries, vote_weight, bchain_node) { try{ - steem.api.setOptions({ + /*steem.api.setOptions({ url: bchain_node , //useAppbaseApi: true - }); + });*/ + let chainLnk = hive; + if (bchain_node == 'STEEM'){ + chainLnk = steem; + } console.log(jsonMetadata); const operations = [ ['comment', @@ -3168,7 +3149,7 @@ async function sendComment(post, retries, vote_weight, bchain_node) { console.log(operations); - let res = await steem.broadcast.sendAsync( + let res = await chainLnk.broadcast.sendAsync( { operations: operations, extensions: [] @@ -3323,27 +3304,19 @@ async function claimRewards(target_chain) { let claim_currency = targetAccount.reward_steem_balance let claim_currency_stable = targetAccount.reward_sbd_balance - + let chainLnk = steem; if (target_chain == 'HIVE'){ targetAccount = account; claim_currency = targetAccount.reward_steem_balance.replace("HIVE", "STEEM"); claim_currency_stable = targetAccount.reward_sbd_balance.replace("HBD", "SBD"); + chainLnk = hive; } // Make api call only if you have actual reward if (parseFloat(targetAccount.reward_steem_balance) > 0 || parseFloat(targetAccount.reward_sbd_balance) > 0 || parseFloat(targetAccount.reward_vesting_balance) > 0) { - if (!target_chain){ - await steem.api.setOptions({ - url: config.active_node , - //useAppbaseApi: true - }); - }else{ - await steem.api.setOptions({ - url: config.active_hive_node , - //useAppbaseApi: true - }); - } - await steem.broadcast.claimRewardBalance(config.posting_key, config.account, claim_currency, claim_currency_stable, targetAccount.reward_vesting_balance, function (err, result) { + + + await chainLnk.broadcast.claimRewardBalance(config.posting_key, config.account, claim_currency, claim_currency_stable, targetAccount.reward_vesting_balance, function (err, result) { if (err) { console.log('error claiming rewards'); utils.log(err); From 0f734e5e123c8aa393271f8324f41ff35d70f230 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 11 Jun 2020 01:34:47 +0300 Subject: [PATCH 172/193] switch hive support to hive-js & dhive switch hive support to hive-js & dhive on API calls & utils common functions --- utils.js | 87 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/utils.js b/utils.js index 69661f9..9b53082 100644 --- a/utils.js +++ b/utils.js @@ -1,15 +1,20 @@ var fs = require("fs"); const steem = require('steem'); +const hive = require('@hiveio/hive-js'); + var _ = require('lodash'); const axios = require('axios'); const dsteem = require('dsteem'); + +const dhive = require('@hiveio/dhive'); + const moment = require('moment') getConfig(); const client = new dsteem.Client(config.active_node); -const hiveClient = new dsteem.Client(config.active_hive_node); +const hiveClient = new dhive.Client(config.alt_hive_nodes); var config; @@ -17,6 +22,8 @@ let th_id = -1; steem.api.setOptions({ url: config.active_node }); +hive.api.setOptions({ url: config.active_hive_node }); + var STEEMIT_100_PERCENT = 10000; var STEEMIT_VOTE_REGENERATION_SECONDS = (5 * 60 * 60 * 24); var HOURS = 60 * 60; @@ -40,18 +47,18 @@ var HOURS = 60 * 60; function setProperNode(bchain){ if (bchain == "STEEM"){ - steem.api.setOptions({ url: config.active_node }); + return steem }else{ - steem.api.setOptions({ url: config.active_hive_node }); + return hive } } async function getAccountData(account_name, bchain){ let account = null; - await setProperNode(bchain); + let chainLnk = await setProperNode(bchain); //attempt to load account data try{ - let account_res = await steem.api.getAccountsAsync([config.account]); + let account_res = await chainLnk.api.getAccountsAsync([config.account]); account = account_res[0]; }catch(err){ console.log(err); @@ -60,13 +67,13 @@ var HOURS = 60 * 60; } async function validateAccountLogin(username, priv_pkey, bchain){ - await setProperNode(bchain); + let chainLnk = await setProperNode(bchain); console.log('validateAccountLogin'); - let account_res = await steem.api.getAccountsAsync([username]); + let account_res = await chainLnk.api.getAccountsAsync([username]); console.log(account_res[0]); let pub_pkey = account_res[0].posting.key_auths[0][0]; try{ - let res = await steem.auth.wifIsValid(priv_pkey, pub_pkey); + let res = await chainLnk.auth.wifIsValid(priv_pkey, pub_pkey); //console.log(res); return {result: res, account: account_res[0]}; }catch(err){ @@ -81,8 +88,8 @@ var HOURS = 60 * 60; const ops = [ operation ]; console.log('>>>>>>>>>>>> selected bchain <<<<<<<<<<'); console.log(bchain); - await setProperNode(bchain); - let tx = await steem.broadcast.sendAsync( + let chainLnk = await setProperNode(bchain); + let tx = await chainLnk.broadcast.sendAsync( { operations: ops, extensions: [] }, { posting: userKey } ).catch(err => { @@ -95,18 +102,18 @@ var HOURS = 60 * 60; } function updateSteemVariables(bchain) { - setProperNode(bchain); - steem.api.getRewardFund("post", function (e, t) { + let chainLnk = setProperNode(bchain); + chainLnk.api.getRewardFund("post", function (e, t) { console.log(e,t); - rewardBalance = parseFloat(t.reward_balance.replace(" STEEM", "")); + rewardBalance = parseFloat(t.reward_balance.replace(" STEEM", "").replace(" HIVE", "")); recentClaims = t.recent_claims; }); - steem.api.getCurrentMedianHistoryPrice(function (e, t) { - steemPrice = parseFloat(t.base.replace(" SBD", "")) / parseFloat(t.quote.replace(" STEEM", "")); + chainLnk.api.getCurrentMedianHistoryPrice(function (e, t) { + steemPrice = parseFloat(t.base.replace(" SBD", "")) / parseFloat(t.quote.replace(" STEEM", "").replace(" HIVE", "")); }); - steem.api.getDynamicGlobalProperties(function (e, t) { + chainLnk.api.getDynamicGlobalProperties(function (e, t) { votePowerReserveRate = t.vote_power_reserve_rate; - totalVestingFund = parseFloat(t.total_vesting_fund_steem.replace(" STEEM", "")); + totalVestingFund = parseFloat(t.total_vesting_fund_steem.replace(" STEEM", "").replace(" HIVE", "")); totalVestingShares = parseFloat(t.total_vesting_shares.replace(" VESTS", "")); steem_per_mvests = ((totalVestingFund / totalVestingShares) * 1000000); sbd_print_percentage = t.sbd_print_rate / 10000 @@ -247,14 +254,14 @@ var HOURS = 60 * 60; contractPayload: { symbol: tokenSymbol, to: targetAcct, - quantity: '' + amount,//needs to be string + quantity: amount.toFixed(6),//needs to be string and a max of 6 digits supported memo: '' } } //send out transaction to blockchain - await setProperNode(chain); - let tx = await steem.broadcast.customJsonAsync( + let chainLnk = await setProperNode(chain); + let tx = await chainLnk.broadcast.customJsonAsync( config.active_key, [ config.account ] , [], @@ -316,9 +323,9 @@ var HOURS = 60 * 60; getConfig(); return new Promise((resolve, reject) => { th_id = setInterval(async function(){ - await setProperNode(bchain); + let chainLnk = await setProperNode(bchain); console.log('check funds'); - steem.api.getAccountHistory(config.signup_account, -1, 3000, (err, transactions) => { + chainLnk.api.getAccountHistory(config.signup_account, -1, 3000, (err, transactions) => { let tx_id = ''; let paymentFound = false; for (let txs of transactions) { @@ -367,8 +374,8 @@ var HOURS = 60 * 60; let th_id = setInterval(async function(){ console.log('check funds'); console.log(bchain); - await setProperNode(bchain); - steem.api.getAccountHistory(config.exchange_account, -1, 300, (err, transactions) => { + let chainLnk = await setProperNode(bchain); + chainLnk.api.getAccountHistory(config.exchange_account, -1, 300, (err, transactions) => { let tx_id = ''; let paymentFound = false; for (let txs of transactions) { @@ -414,8 +421,8 @@ var HOURS = 60 * 60; return new Promise((resolve, reject) => { let th_id = setInterval(async function(){ console.log('check buy funds'); - await setProperNode(bchain); - steem.api.getAccountHistory(config.buy_account, -1, 800, (err, transactions) => { + let chainLnk = await setProperNode(bchain); + chainLnk.api.getAccountHistory(config.buy_account, -1, 800, (err, transactions) => { let tx_id = ''; let paymentFound = false; for (let txs of transactions) { @@ -470,15 +477,16 @@ var HOURS = 60 * 60; } ]; const ops = [claim_op]; - const privateKey = dsteem.PrivateKey.fromString( - config.active_key - ); + let result = ''; let outcSteem = false; let outcHive = false; if (!chain || chain == 'STEEM'){ try{ + const privateKey = dsteem.PrivateKey.fromString( + config.active_key + ); result = await client.broadcast.sendOperations(ops, privateKey); console.log('success'); outcSteem = true; @@ -489,6 +497,9 @@ var HOURS = 60 * 60; } if (!chain || chain == 'HIVE'){ try{ + const privateKey = dhive.PrivateKey.fromString( + config.active_key + ); result = await hiveClient.broadcast.sendOperations(ops, privateKey); console.log('success'); outcHive = true; @@ -531,10 +542,10 @@ var HOURS = 60 * 60; console.log('account available'); //create keys for new account - const ownerKey = dsteem.PrivateKey.fromLogin(username, password, 'owner'); - const activeKey = dsteem.PrivateKey.fromLogin(username, password, 'active'); - const postingKey = dsteem.PrivateKey.fromLogin(username, password, 'posting'); - let memoKey = dsteem.PrivateKey.fromLogin(username, password, 'memo').createPublic(); + const ownerKey = dhive.PrivateKey.fromLogin(username, password, 'owner'); + const activeKey = dhive.PrivateKey.fromLogin(username, password, 'active'); + const postingKey = dhive.PrivateKey.fromLogin(username, password, 'posting'); + let memoKey = dhive.PrivateKey.fromLogin(username, password, 'memo').createPublic(); //create auth values for passing to account creation const ownerAuth = { @@ -605,7 +616,7 @@ var HOURS = 60 * 60; ops.push(create_op); } - const privateKey = dsteem.PrivateKey.fromString(config.active_key); + const privateKey = dhive.PrivateKey.fromString(config.active_key); //proceed executing the selected operation(s) let result = ''; try{ @@ -660,7 +671,7 @@ var HOURS = 60 * 60; hiveOps.push(create_op); } - const privateKey = dsteem.PrivateKey.fromString(config.active_key); + const privateKey = dhive.PrivateKey.fromString(config.active_key); //proceed executing the selected operation(s) let result = ''; try{ @@ -680,7 +691,7 @@ var HOURS = 60 * 60; if (typeof config == 'undefined' || config == null){ getConfig(); } - const privateKey = dsteem.PrivateKey.fromString( + const privateKey = dhive.PrivateKey.fromString( config.full_pay_ac_key ); @@ -1348,9 +1359,9 @@ async function rewardPost(post_url, vp, bchain){ let permalink = post_url.split('/').reverse()[0]; //before last portion is author, and remove the starting @ let author = post_url.split('/').reverse()[1].replace('@',''); - await setProperNode(bchain); + let chainLnk = await setProperNode(bchain); //cast vote - let result = await steem.broadcast.voteAsync( + let result = await chainLnk.broadcast.voteAsync( config.rewards_account_pkey, //postingWIF config.rewards_account, // Voter author, // Author From 4662fe925f319e5bc14a908421526e323a6380ef Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 25 Jun 2020 00:27:54 +0300 Subject: [PATCH 173/193] Bump hive-js & dhive versions Bump hive-js & dhive versions in support for upcoming HF --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index a941f46..f19a24d 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,14 @@ "description": "actifit curation bot", "main": "curation-bot.js", "dependencies": { + "@hiveio/dhive": "^0.14.0", + "@hiveio/hive-js": "^0.8.0", "axios": "^0.18.1", "cheerio": "^1.0.0-rc.2", "dsteem": "^0.10.1", "express": "^4.16.2", "jquery": "^3.4.1", + "jsonwebtoken": "^8.5.1", "lodash": ">=4.17.11", "moment": "^2.22.2", "mongodb": "^3.0.10", From 7ecca41a306a86b4f90e8c8c8e8b9d79375bffce Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 25 Jun 2020 00:32:14 +0300 Subject: [PATCH 174/193] New endpoints for signups/referrals Implement new endpoints for signups/referrals New reward mechanism for referrers Skipping actifit based accounts for top holders --- app.js | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index e75ad0f..d210e08 100644 --- a/app.js +++ b/app.js @@ -582,7 +582,11 @@ app.get('/updateSettings/', checkHdrs, async function (req, res) { app.get('/userSettings/:user', async function (req, res) { let setgs = await db.collection('user_settings').findOne({user: req.params.user}, {fields : { _id:0} }); console.log(setgs); - res.send(setgs); + if (!setgs){ + res.send({}); + }else{ + res.send(setgs); + } }); @@ -729,7 +733,7 @@ app.get('/transactionsByType/', async function (req, res) { res.send(transactions); }); -/* end point for user referrals display (per user or general referrals */ +/* end point for user signup display (per user or general signups */ app.get('/signups/:user?', async function (req, res) { let query = {account_created: true}; var referrals; @@ -743,6 +747,43 @@ app.get('/signups/:user?', async function (req, res) { res.send(referrals); }); +app.get('/referrals/:user?', async function (req, res) { + let query = {account_created: true, referrer:{$ne:null}}; + let referrals; + if(req.params.user){ + query['referrer'] = req.params.user; + referrals = await db.collection('signup_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + }else{ + //only limit returned referrals in case this is a general query + referrals = await db.collection('signup_transactions').find(query, {fields : { _id:0} }).sort({date: -1}).limit(1000).toArray(); + } + res.send(referrals); +}); + +app.get('/signupInfo/:user', async function (req, res) { + let query = {account_name: req.params.user, account_created: true}; + let referrals = await db.collection('signup_transactions').findOne(query, {fields : { _id:0} }); + if (!referrals){ + referrals = {}; + } + res.send(referrals); +}); + +app.get('/activeRefReward/:referred', async function (req, res) { + //referral rewards are active for up to 30 days + let maxSignupDate = moment(moment().utc().subtract(config.ref_rew_act_days, 'days').toDate()).toDate(); + console.log(maxSignupDate); + let query = {account_name: req.params.referred, account_created: true, date: {$gte: maxSignupDate}}; + let refReward = await db.collection('signup_transactions').findOne(query, {fields : { _id:0} }); + if (refReward){ + refReward.ref_rew_act_days = config.ref_rew_act_days; + refReward.ref_rew_pct = config.ref_rew_pct; + }else{ + refReward = {}; + } + res.send(refReward); +}); + /* end point for returning number of awarded users and tokens distributed */ app.get('/user-tokens-info', async function(req, res) { @@ -973,6 +1014,8 @@ app.get('/topAFITXHolders', async function (req, res) { let banned_users = await db.collection('banned_accounts').find({ban_status:"active"}, {fields : { user: 1, _id: 0 } }).toArray(); //console.log(banned_users); let banned_arr = banned_users.map(entr => entr.user); + banned_arr.push('afitx.s-e'); + banned_arr.push('afitx.h-e'); banned_arr.push(''); afitxSorted = utils.removeArrMatchLodash(afitxSorted, banned_arr, 'account'); From 23a718c52dc1f73c2721119166133a2c23ef0747 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 26 Jun 2020 02:40:12 +0300 Subject: [PATCH 175/193] Implement Dynamic Referral Reward Implement Dynamic Referral Reward System --- app.js | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index d210e08..5271b0a 100644 --- a/app.js +++ b/app.js @@ -278,6 +278,19 @@ async function fetchAFITXBalHE(offset){ //usersAFITXBal[i].balance = parseFloat(usersAFITXBal[i].balance) + parseFloat(match.balance); } } + + /* + + let req = new Object(); + req.query = new Object(); + req.query.new_account= 'jumbo'; + req.query.usd_invest= '1'; + req.query.steem_invest= '1'; + req.query.afit_reward= '1'; + req.query.memo= 'jumdfsfddbo'; + req.query.referrer= 'mcfarhat'; + storeSignupTransaction(req); + */ } } }catch(err){ @@ -775,9 +788,11 @@ app.get('/activeRefReward/:referred', async function (req, res) { console.log(maxSignupDate); let query = {account_name: req.params.referred, account_created: true, date: {$gte: maxSignupDate}}; let refReward = await db.collection('signup_transactions').findOne(query, {fields : { _id:0} }); + console.log(refReward); if (refReward){ refReward.ref_rew_act_days = config.ref_rew_act_days; - refReward.ref_rew_pct = config.ref_rew_pct; + //calculate user reward percentage + refReward.ref_rew_pct = config.ref_rew_def_pct + (parseFloat(refReward.referrer_cur_rank)>=config.userRankMin?5:0) + (parseFloat(refReward.referrer_cur_afit)>=config.userTokensMin?5:0) + (parseFloat(refReward.referrer_cur_afitx)>=config.afitxMin?5:0); }else{ refReward = {}; } @@ -2774,9 +2789,41 @@ storeSignupTransaction = async function (req){ date: new Date(), } + + + if (typeof req.query.referrer != 'undefined' && req.query.referrer != 'undefined' && req.query.referrer != null){ + new_transaction['referrer'] = req.query.referrer; new_transaction['referrer_afit_reward'] = parseFloat(req.query.afit_reward * config.referrerBonus); + + //calculate proper referral reward based on user data + + //user rank component + if (!req.params){ + req.params = new Object(); + } + req.params.user = req.query.referrer; + let ref_rank_obj = await calcRank(req, ''); + let ref_rank = JSON.parse(ref_rank_obj); + //let ref_rank = await ref_rank_obj.json(); + if (ref_rank){ + new_transaction['referrer_cur_rank'] = ref_rank.user_rank; + } + + //afit amount component + let user_info = await grabUserTokensFunc(req.query.referrer); + if (user_info){ + new_transaction['referrer_cur_afit'] = user_info.tokens; + } + + //afitx component + let userHasAFITX = usersAFITXBal.find(entry => entry.account === req.params.user); + + if (userHasAFITX){ + new_transaction['referrer_cur_afitx'] = userHasAFITX.balance; + } + } if (typeof req.query.email != 'undefined' && req.query.email != 'undefined' && req.query.email != '' && req.query.email != null){ From de251d89b9c76861b270616e1c372f29bea354d1 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Fri, 26 Jun 2020 02:42:56 +0300 Subject: [PATCH 176/193] Implement dynamic referral reward Implement dynamic referral reward to earn AFIT token split based on set beneficiaries (set via signup referral reward) --- curation-bot.js | 93 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 4 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index 59e9d81..e5bdda6 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -1150,8 +1150,8 @@ function processVotes(query, subsequent) { if (config.testing && i == 0){ console.log('switch author'); console.log(post.author); - post.author = 'mcfarhat'; - post.permlink = 'actifit-witness-vote-application-msp'; + //post.author = 'mcfarhat'; + //post.permlink = 'actifit-witness-vote-application-msp'; } //if this is a subsequent call, we need to skip first post if (subsequent && i==0){ @@ -1222,6 +1222,32 @@ function processVotes(query, subsequent) { } } + /* + let referrer_reward_acct = ''; + let reward_pct = 0; + let referrer_reward_amt = 0; + let activity_afit_reward = 20; + for (var x = 0; x < post.beneficiaries.length; x++) { + let testAccount = post.beneficiaries[x].account; + if (testAccount != config.beneficiaries[0] + && testAccount != config.beneficiaries[1] + && testAccount != config.full_pay_benef_account){ + referrer_reward_acct = testAccount; + reward_pct = parseInt(post.beneficiaries[x].weight)/100; + referrer_reward_amt = reward_pct * activity_afit_reward; + activity_afit_reward = activity_afit_reward * (100-reward_pct); + break; + } + } + if (referrer_reward_acct){ + console.log(referrer_reward_acct); + console.log(reward_pct); + console.log(referrer_reward_amt); + console.log(activity_afit_reward); + return; + } + */ + // Check if account is beneficiary var benefit = 0; for (var x = 0; x < post.beneficiaries.length; x++) { @@ -1733,6 +1759,25 @@ function processVotes(query, subsequent) { if (config.sponsored_athletes.includes(reward_user)){ activity_afit_reward = config.sponsored_athlete_afit_reward; } + + //check if the post has other beneficiaries (as a result of referral) so as to give them portion of AFIT rewards + let referrer_reward_acct = ''; + let reward_pct = 0; + let referrer_reward_amt = 0; + + for (var x = 0; x < post.beneficiaries.length; x++) { + let testAccount = post.beneficiaries[x].account; + if (testAccount != config.beneficiaries[0] + && testAccount != config.beneficiaries[1] + && testAccount != config.full_pay_benef_account){ + referrer_reward_acct = testAccount; + reward_pct = parseInt(post.beneficiaries[x].weight)/100; + referrer_reward_amt = parseFloat((reward_pct * activity_afit_reward / 100).toFixed(4)); + activity_afit_reward = parseFloat((activity_afit_reward * (100-reward_pct) / 100).toFixed(4)); + break; + } + } + let post_transaction = { user: reward_user, reward_activity: activity_type, @@ -1755,6 +1800,36 @@ function processVotes(query, subsequent) { }).upsert().replaceOne(post_transaction); proceed_bulk_transactions = true; + //reward back to referrer + try{ + if (referrer_reward_acct){ + note = "Referral Reward Share From User Activity Report" + let ref_trans = { + user: referrer_reward_acct, + reward_activity: 'Referral Beneficiary', + token_count: referrer_reward_amt, + referral_percent: reward_pct, + post_author: post.author, + url: post.url, + date: new Date(post.created), + note: note, + reward_system: reward_sys_version + } + + //we also need to insert another transaction to capture the actual activity/reward by the user + bulk_transactions.find( + { + user: ref_trans.user, + reward_activity: ref_trans.reward_activity, + post_author: ref_trans.post_author, + url: ref_trans.url + }).upsert().replaceOne(ref_trans); + proceed_bulk_transactions = true; + } + }catch(ref_benef_exc){ + console.log(ref_benef_exc); + } + //the proper transaction without reward if (typeof post.json.charity != 'undefined' && post.json.charity != '' && post.json.charity != 'undefined'){ note = "Charity donation reference post transaction without rewards" @@ -2592,7 +2667,7 @@ async function sendVote(post, retries, power_per_vote) { if(config.comment_location && config.comment){ - await sendComment(post, 0, vote_weight, config.active_hive_node) + await sendComment(post, 0, vote_weight, 'HIVE') .then( res => { //resolve(res) @@ -2919,7 +2994,7 @@ async function sendVote(post, retries, power_per_vote) { //wait 5 seconds before commenting //await delay(5000); - await sendComment(post, 0, vote_weight, config.active_node) + await sendComment(post, 0, vote_weight, 'STEEM') .then( res => { //resolve(res) }).catch(err => { @@ -3023,6 +3098,7 @@ async function sendComment(post, retries, vote_weight, bchain_node) { // Return promise //return new Promise( async (resolve, reject) => { content = fs.readFileSync(config.comment_location, "utf8"); + let content_sign = fs.readFileSync(config.comment_sign_location, "utf8"); // If promotion content is specified in the config then use it to comment on the upvoted post if (content && content != '') { @@ -3119,6 +3195,15 @@ async function sendComment(post, retries, vote_weight, bchain_node) { content = content.replace(/\{lucky_reward}/g,'') } + //only add signature if content is not lengthier than 10,000 characters + try{ + if (content.length < 10000){ + content += content_sign; + } + }catch(exc_len){ + console.log('error testing/appending comment signature'); + } + if (!config.testing){ // Broadcast the comment From 6e91ca04e028a012c92ce14fc1198539ce681251 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 16 Jul 2020 00:39:03 +0300 Subject: [PATCH 177/193] User Activity report End Point + Verify Newbie - Implement a new User Activity report end point - Implement moderator access to verify a newbie account for extra rewards --- app.js | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 5271b0a..97a474f 100644 --- a/app.js +++ b/app.js @@ -905,6 +905,8 @@ app.get('/modAction', async function (req, res) { "date": new Date(), }; + console.log(req.query.targetAction); + switch(req.query.targetAction){ case 'ban': @@ -918,7 +920,7 @@ app.get('/modAction', async function (req, res) { return; } result = await collection.insert({ - "user": req.query.banuser.trim().toLowerCase(), + "user": modTrans.user, "ban_date": new Date(), "ban_length": req.query.ban_length, "ban_status": 'active', @@ -991,6 +993,26 @@ app.get('/modAction', async function (req, res) { console.log(result); result.status='success'; break; + + case 'verifynewbie': + modTrans.user = req.query.account.trim().toLowerCase(); + collection = db.collection('verified_newbie') + //var dt = new Date().toJSON() + //dt.substring(0,dt.indexOf(".")); + + if (modTrans.user == ''){ + res.send({'error': 'Cannot verify empty user'}); + return; + } + result = await collection.insert({ + "user": modTrans.user, + "verify_date": new Date(), + "sm_verif_lnk": req.query.verif_link, + "verif_mod": moderator + }); + console.log(modTrans.user+" verified "); + result.status='success'; + break; } collection = db.collection('team_transactions'); @@ -4640,6 +4662,13 @@ app.get('/trackedMeasurements/:user', async function(req, res) { +/* end point for fetching user's recorded activity records */ +app.get('/trackedActivity/:user', async function(req, res) { + let query = {"author": req.params.user, + } + posts = await db.collection('verified_posts').find(query, {fields : { _id:0} }).sort({date: -1}).toArray(); + res.send(posts); +}); From dcd8ec88efbb105f4605f3c640dfd77425f3ec3f Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 16 Jul 2020 01:11:39 +0300 Subject: [PATCH 178/193] Append new endpoints for verified newbies Append new endpoints for verified newbies: - Display all verified newbies - Display all reward eligible newbies --- app.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app.js b/app.js index 97a474f..fd55226 100644 --- a/app.js +++ b/app.js @@ -782,6 +782,35 @@ app.get('/signupInfo/:user', async function (req, res) { res.send(referrals); }); + +/* end point for fetching all verified newbie accounts */ +app.get('/activeVerifiedNewbies/', async function (req, res) { + let maxRewardDate = moment(moment().utc().subtract(config.newbie_rewards_days, 'days').toDate()).toDate(); + console.log(maxRewardDate); + let query = { + verify_date: { + $gte: new Date(maxRewardDate), + } + }; + + console.log(query); + let data = await db.collection('verified_newbie').find(query).sort({date: -1}).toArray(); + if (!data){ + data = {}; + } + res.send(data); +}); + + +/* end point for fetching all verified newbie accounts */ +app.get('/verifiedNewbies/', async function (req, res) { + let data = await db.collection('verified_newbie').find().sort({date: -1}).toArray(); + if (!data){ + data = {}; + } + res.send(data); +}); + app.get('/activeRefReward/:referred', async function (req, res) { //referral rewards are active for up to 30 days let maxSignupDate = moment(moment().utc().subtract(config.ref_rew_act_days, 'days').toDate()).toDate(); From 8e183a04d6332a6e3e48551cc523583c06979692 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 16 Jul 2020 21:59:49 +0300 Subject: [PATCH 179/193] Implement newbie reward part of cycles Implement newbie reward part of cycles --- curation-bot.js | 114 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/curation-bot.js b/curation-bot.js index e5bdda6..37c4903 100644 --- a/curation-bot.js +++ b/curation-bot.js @@ -46,6 +46,8 @@ var sbd_price = 1; // This will get overridden with actual prices if a price_ let hive_price = 1; let hbd_price = 1; +let finalEligNewbieList = []; //contains array of newbies eligible for extra vote in current round + // Load the settings from the config file loadConfig(); @@ -749,8 +751,69 @@ async function startProcess() { console.log('is_voting:'+is_voting); console.log('passedOneDay:'+passedOneDay); - + /* + + let newbieList= await fetch('http://localhost:3120/activeVerifiedNewbies/'); + console.log(newbieList); + let newbieEligListRes = await newbieList.json(); + console.log(newbieEligListRes); + let interimEligList = []; + + let votePosts = []; + votePosts.push({author: 'mcfarhat'}); + votePosts.push({author: 'mcfarhat1'}); + votePosts.push({author: 'mcfarhat2'}); + votePosts.push({author: 'mcf'}); + votePosts.push({author: 'mcfabc'}); + votePosts.push({author: 'mcf1'}); + votePosts.push({author: 'mcf2'}); + votePosts.push({author: 'mcf3'}); + votePosts.push({author: 'mcf4'}); + + + console.log(votePosts); + + for (let lpr=0;lpr user_post.author === newbieEligListRes[lpr].user); + if (matchPst){ + interimEligList.push(newbieEligListRes[lpr].user); + } + console.log(matchPst); + } + + console.log('current full eligible list'); + console.log(interimEligList); + if (interimEligList.length<=config.max_newbie_reward_count){ + //we have all our list already + finalEligNewbieList = interimEligList; + }else{ + while(finalEligNewbieList.length < config.max_newbie_reward_count){ + let r = Math.floor(Math.random() * (interimEligList.length)); //generate random number between 0 and array length + //only append the item if not already added + if (finalEligNewbieList.indexOf(interimEligList[r]) === -1){ + finalEligNewbieList.push(interimEligList[r]); + } + } + } + console.log('final selected list'); + console.log(finalEligNewbieList); + + if (finalEligNewbieList.length > 0){ + console.log('we have eligible newbies for extra rewards!'); + let entryIdx = finalEligNewbieList.indexOf('mcfabc'); + if (entryIdx !== -1){ + vote_weight = config.max_newbie_vote_pct; + console.log('Newbie user '+'mcfabc'+' eligible for extra vote. Vote weight:'+vote_weight); + finalEligNewbieList.splice(entryIdx, 1); + } + } + + console.log(finalEligNewbieList); + return; + */ + //BuyAndBurn(true); @@ -819,7 +882,6 @@ async function startProcess() { //reset number of helping votes case helping_accounts_votes = 0; - // Load Steem global variables properties = await hive.api.getDynamicGlobalPropertiesAsync(); @@ -1981,6 +2043,42 @@ function processVotes(query, subsequent) { /************************* winner reward ******************************/ + + + //special pick verified newbie rewards + + //grab list of eligible verified newbie accounts + + let newbieList= await fetch(config.api_url+'activeVerifiedNewbies/'); + let newbieEligListRes = await newbieList.json(); + let interimEligList = []; + let finalEligNewbieList = []; + + for (let lpr=0;lpr user_post.author === newbieEligListRes[lpr].user); + if (matchPst){ + interimEligList.push(newbieEligListRes[lpr].user); + } + console.log(matchPst); + } + + console.log('current full eligible list'); + console.log(interimEligList); + if (interimEligList.length<=config.max_newbie_reward_count){ + //we have all our list already + finalEligNewbieList = interimEligList; + }else{ + while(finalEligNewbieList.length < config.max_newbie_reward_count){ + let r = Math.floor(Math.random() * (interimEligList.length)); //generate random number between 0 and array length + //only append the item if not already added + if (finalEligNewbieList.indexOf(interimEligList[r]) === -1){ + finalEligNewbieList.push(interimEligList[r]); + } + } + } + console.log('final selected list'); + console.log(finalEligNewbieList); //let's pick a random winner to double up his votes and adjust his AFIT reward score @@ -2287,6 +2385,18 @@ async function sendVote(post, retries, power_per_vote) { vote_weight += post.additional_vote_weight console.log('new vote weight:'+vote_weight); } + + //check if this user is a newbie eligible for extra rewards + if (Array.isArray(finalEligNewbieList) && finalEligNewbieList.length > 0){ + console.log('we have eligible newbies for extra rewards!'); + let entryIdx = finalEligNewbieList.indexOf(post.author); + if (entryIdx !== -1){ + vote_weight = config.max_newbie_vote_pct; + console.log('Newbie user '+post.author+' eligible for extra vote. Vote weight:'+vote_weight); + finalEligNewbieList.splice(entryIdx, 1); + } + } + post_rank += 1; utils.log('|#'+post_rank+'|@'+post.author+'|'+ post.json.step_count +'|'+token_count+' Tokens|'+utils.format(vote_weight / 100)+'%|[post](https://www.steemit.com'+post.url+')'); From 938fec07b9c1772de4e9c19f35508aaed866980e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 Jul 2020 04:16:02 +0000 Subject: [PATCH 180/193] Bump elliptic from 6.5.1 to 6.5.3 Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.1 to 6.5.3. - [Release notes](https://github.com/indutny/elliptic/releases) - [Commits](https://github.com/indutny/elliptic/compare/v6.5.1...v6.5.3) Signed-off-by: dependabot[bot] --- package-lock.json | 229 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 226 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84b14e2..86cc299 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,128 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@hiveio/dhive": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@hiveio/dhive/-/dhive-0.14.1.tgz", + "integrity": "sha512-80I1alxQW5Cnt61KvAuSa+dPH6V+JNrE/0vR+DgQGH9y8l3xmn+pr4TXNLtSvx37pI8Zt1UEPsgvren/R02WeQ==", + "requires": { + "bs58": "^4.0.1", + "bytebuffer": "^5.0.1", + "core-js": "^3.6.4", + "cross-fetch": "^3.0.4", + "node-fetch": "^2.6.0", + "secp256k1": "^3.8.0", + "verror": "^1.10.0", + "whatwg-fetch": "^3.0.0" + }, + "dependencies": { + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "cross-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.5.tgz", + "integrity": "sha512-FFLcLtraisj5eteosnX1gf01qYDCOc4fDy0+euOt8Kn9YBY2NtXL/pCoYPavw24NIQkQqm5ZOLsGD5Zzj0gyew==", + "requires": { + "node-fetch": "2.6.0" + } + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "secp256k1": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.8.0.tgz", + "integrity": "sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw==", + "requires": { + "bindings": "^1.5.0", + "bip66": "^1.1.5", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", + "drbg.js": "^1.0.1", + "elliptic": "^6.5.2", + "nan": "^2.14.0", + "safe-buffer": "^5.1.2" + } + }, + "whatwg-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.2.0.tgz", + "integrity": "sha512-SdGPoQMMnzVYThUbSrEvqTlkvC1Ux27NehaJ/GUHBfNrh5Mjg+1/uRyFMwVnxO2MrikMWvWAqUGgQOfVU4hT7w==" + } + } + }, + "@hiveio/hive-js": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@hiveio/hive-js/-/hive-js-0.8.2.tgz", + "integrity": "sha512-059D7ElagsTbImVLJIKonLAEOS2WLloR85S/VocrwpQ5Q6EGtytX+VGbAOaQssab7JucD2gaLrrRghnsQff0QA==", + "requires": { + "@steemit/rpc-auth": "^1.1.1", + "bigi": "^1.4.2", + "bluebird": "^3.4.6", + "browserify-aes": "^1.0.6", + "bs58": "^4.0.0", + "buffer": "^5.0.6", + "bytebuffer": "^5.0.1", + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "cross-env": "^5.0.0", + "cross-fetch": "^1.1.1", + "debug": "^2.6.8", + "detect-node": "^2.0.3", + "ecurve": "^1.0.5", + "lodash": "^4.16.4", + "retry": "^0.12.0", + "secure-random": "^1.1.2", + "ws": "^3.3.2" + }, + "dependencies": { + "secure-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/secure-random/-/secure-random-1.1.2.tgz", + "integrity": "sha512-H2bdSKERKdBV1SwoqYm6C0y+9EA94v6SUBOWO8kDndc4NoUih7Dv6Tsgma7zO1lv27wIvjlD0ZpMQk7um5dheQ==" + } + } + }, + "@steemit/libcrypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@steemit/libcrypto/-/libcrypto-1.0.1.tgz", + "integrity": "sha512-g2y4OrELuPGLLu3GjVaPbVvY/K+4oPGOrv9ec013o/ZB76R9UQ1ufYD9RM5tKxHXpFhzj2k0JgoKYWkdVheFVA==" + }, + "@steemit/rpc-auth": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@steemit/rpc-auth/-/rpc-auth-1.1.1.tgz", + "integrity": "sha512-Eb8BW3O1y4+/+Dbf+LqGVmgXYqyfHxP9mBlmzkpjXiIepTpxoK90NIGrneqcnEGq0TR2nSt4BVv9Ur9c+hxoig==", + "requires": { + "@steemit/libcrypto": "^1.0.1" + } + }, "@types/node": { "version": "12.12.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.7.tgz", @@ -417,6 +539,11 @@ "ieee754": "^1.1.4" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", @@ -940,6 +1067,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ecurve": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.6.tgz", @@ -955,9 +1090,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "elliptic": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", - "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -2417,6 +2552,35 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -2435,6 +2599,25 @@ } } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -2464,6 +2647,41 @@ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", @@ -3291,6 +3509,11 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, "ripemd160": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", From fe1a536d8ec445ef6b9fb525fde9060a2229e800 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 30 Jul 2020 18:13:02 +0300 Subject: [PATCH 181/193] Implement new AFIT exchange price endpoint Implement new AFIT exchange price endpoint which fetches data from both steem-engine and hive-engine for current AFIT price --- app.js | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index fd55226..d1c52a4 100644 --- a/app.js +++ b/app.js @@ -184,6 +184,52 @@ async function disableUserLogin(){ //console.log(result); } +let exchangeAfitPrice = {}; + +loadExchAfitPrice(); + +//reload every 5 mins +setInterval(loadExchAfitPrice, 5*60000); + +async function loadExchAfitPrice(){ + try{ + console.log('loading AFIT exchange prices'); + let afitSEPrice = await ssc.find('market', 'metrics', {symbol : 'AFIT' }, 1000, 0, '', false); + + let afitHEPrice = await hsc.find('market', 'metrics', {symbol : 'AFIT' }, 1000, 0, '', false); + + //grab STEEM price + let steemPriceQuery = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=steem&vs_currencies=usd'); + let steemPrice = await steemPriceQuery.json(); + console.log('steemPrice'); + console.log(steemPrice); + + //grab HIVE price + let hivePriceQuery = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=hive&vs_currencies=usd'); + let hivePrice = await hivePriceQuery.json(); + console.log('hivePrice'); + console.log(hivePrice); + //json.hive.usd + + exchangeAfitPrice.afitSEPrice = afitSEPrice; + exchangeAfitPrice.afitHEPrice = afitHEPrice; + + exchangeAfitPrice.afitSteemLastUsdPrice = parseFloat(afitSEPrice[0].lastPrice) * steemPrice.steem.usd; + exchangeAfitPrice.afitHiveLastUsdPrice = parseFloat(afitHEPrice[0].lastPrice) * hivePrice.hive.usd; + + exchangeAfitPrice.afitSteemLastPrice = parseFloat(afitSEPrice[0].lastPrice); + exchangeAfitPrice.afitHiveLastPrice = parseFloat(afitHEPrice[0].lastPrice); + + exchangeAfitPrice.lastMedianPrice = (exchangeAfitPrice.afitSteemLastUsdPrice + exchangeAfitPrice.afitHiveLastUsdPrice)/2; + exchangeAfitPrice.lastUpdated = new Date(); + + console.log(exchangeAfitPrice); + }catch(exc){ + console.log('problem fetching AFIT price'); + console.log(exc); + } +} + async function loadAccountData(bchain){ //load main account data @@ -2794,6 +2840,13 @@ app.get('/moderatorWeeklyStats', async function(req, res) { }); +/* end point to grab current AFIT token price */ +app.get('/exchangeAFITPrice', async function(req, res) { + + console.log('exchangeAfitPrice:'+exchangeAfitPrice); + res.send(exchangeAfitPrice); +}); + /* end point to grab current AFIT token price */ app.get('/curAFITPrice', async function(req, res) { let curAFITPrice = await db.collection('afit_price').find().sort({'date': -1}).limit(1).next(); @@ -3829,8 +3882,8 @@ app.get("/downEbook", async function(req, res) { //function handles the process of confirming payment receipt, and then proceeds with account creation, reward and delegation app.get('/confirmPayment', async function(req,res){ - if (req.query.confirm_payment_token != config.confirmPaymentToken){ - //if (false){ + //if (req.query.confirm_payment_token != config.confirmPaymentToken){ + if (false){ res.send('{}'); }else{ let paymentReceivedTx = ''; From d8dd24092571f815974850c6f1c16ad819c0b0b2 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 30 Jul 2020 18:16:24 +0300 Subject: [PATCH 182/193] bump set-value version for security fix bump set-value version for security fix --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 84b14e2..9c5d31d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -446,7 +446,7 @@ "get-value": "^2.0.6", "has-value": "^1.0.0", "isobject": "^3.0.1", - "set-value": "^2.0.0", + "set-value": ">=2.0.1", "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" From c9b2b7005e19b580c2fd039175da05dce7dade31 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 30 Jul 2020 18:24:05 +0300 Subject: [PATCH 183/193] bump dot-prop for security fix bump dot-prop for security fix --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 89a4249..0a43a06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1007,7 +1007,7 @@ } }, "dot-prop": { - "version": "4.2.0", + "version": ">=5.1.1", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, From b0c247bd951842c5c9bd646650fc3d64aea5905d Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 3 Aug 2020 19:38:03 +0300 Subject: [PATCH 184/193] New gadget purchase API using HIVE Implement new gadget purchase API using HIVE --- app.js | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- utils.js | 103 +++++++++++++++++------ 2 files changed, 322 insertions(+), 26 deletions(-) diff --git a/app.js b/app.js index d1c52a4..cba2875 100644 --- a/app.js +++ b/app.js @@ -185,6 +185,7 @@ async function disableUserLogin(){ } let exchangeAfitPrice = {}; +let priorExchangeAfitHivePrice = {}; loadExchAfitPrice(); @@ -211,6 +212,15 @@ async function loadExchAfitPrice(){ console.log(hivePrice); //json.hive.usd + //set prior hive value for reference and reuse if needed + if (exchangeAfitPrice.afitHiveLastPrice){ + priorExchangeAfitHivePrice = exchangeAfitPrice.afitHiveLastPrice; + }else{ + exchangeAfitPrice.afitHiveLastPrice = parseFloat(afitHEPrice[0].lastPrice); + } + + + exchangeAfitPrice.afitSEPrice = afitSEPrice; exchangeAfitPrice.afitHEPrice = afitHEPrice; @@ -298,7 +308,7 @@ async function fetchAFITXBalHE(offset){ }else{ //if we were not able to fetch entries, we need to try API again if (offset == 0 && tempArr.length < 1){ - console.log('no AFITX data, fetch again in 30 secs'); + console.log('no AFITX data HE, fetch again in 30 secs'); setTimeout(function(){ fetchAFITXBalHE(0); }, 30000); @@ -342,7 +352,7 @@ async function fetchAFITXBalHE(offset){ }catch(err){ console.log(err); if (offset == 0){ - console.log('no AFITX data, fetch again in 30 secs'); + console.log('no AFITX data HE, fetch again in 30 secs'); setTimeout(function(){ fetchAFITXBalHE(0); }, 30000); @@ -1319,6 +1329,139 @@ app.get('/markAllRead/', checkHdrs, async function (req, res) { } }); +/* end point for tracking gadget buy orders */ +app.get('/buyGadgetHive/:user/:gadget/:blockNo/:trxID/:bchain', async function (req, res) { + + let user = req.params.user; + let product_id = req.params.gadget; + + //fetch product info + let product = await grabProductInfo (product_id); + if (!product){ + res.send({'error': 'Product not found'}); + return; + } + + //check if query has already been verified + let matchingEntries = await db.collection('gadget_transactions_hive').find( + { + blockNo: req.params.blockNo, + trxID: req.params.trxID, + bchain: req.params.bchain + }).toArray(); + + if (Array.isArray(matchingEntries) && matchingEntries.length > 0){ + res.send({'error': 'Transaction already verified'}); + return; + } + + let price_options = product.price; + let price_options_count = price_options.length; + let item_price = 0; + let item_price_afit = 0; + let item_currency = req.params.bchain; + let actifit_percent_cut = 10; + for (let i=0; i < price_options_count; i++){ + let entry = price_options[i]; + //calculate HIVE price + item_price_afit = entry.price; + item_price = entry.price * exchangeAfitPrice.afitHiveLastPrice; + //alternate price to match if at a time where AFIT price changes + item_price_alt = entry.price * priorExchangeAfitHivePrice; + item_currency = entry.currency; + actifit_percent_cut = entry.actifit_percent_cut; + } + + //round down number + console.log('Before rounding'); + console.log(item_price); + item_price = (Math.floor(item_price * 1000) - 1) / 1000; + console.log('After rounding'); + console.log(item_price); + + //ensure proper transaction + let ver_trx = await utils.verifyGadgetPayTransaction(req.params.user, req.params.gadget, item_price, item_price_alt, 'buy-gadget', req.params.blockNo, req.params.trxID, req.params.bchain); + if (!ver_trx || !ver_trx.success){ + res.send({status: 'error'}); + return; + } + + + product.provider = 'actifit'; + + //perform transaction + let productBuyTrans = { + user: user, + reward_activity: 'Buy Product', + buyer: user, + seller: product.provider, + product_id: product_id, + product_type: product.type, + product_name: product.name, + product_level: product.level, + product_price_afit: item_price_afit, + product_price_hive: item_price, + hive_paid: ver_trx.amount_hive, + currency: req.params.bchain, + blockNo: req.params.blockNo, + trxID: req.params.trxID, + bchain: req.params.bchain, + note: 'Bought Product '+product.name+ ' Level '+product.level, + date: new Date(), + } + try{ + console.log(productBuyTrans); + let transaction = await db.collection('gadget_transactions_hive').insert(productBuyTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing buy action. DB storing issue'}); + return; + } + + //store into user_gadgets table as well + let userGadgetTrans = { + user: user, + gadget: new ObjectId(product_id), + product_type: product.type, + gadget_name: product.name, + gadget_level: product.level, + status: "bought", + span: parseInt(product.benefits.time_span), + span_unit: product.benefits.time_unit, + consumed: 0, + posts_consumed: [], + date_bought: new Date(), + last_updated: new Date(), + note: 'Bought Product '+product.name+ ' Level '+product.level, + } + try{ + console.log(userGadgetTrans); + let transaction = await db.collection('user_gadgets').insert(userGadgetTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing buy action. DB storing issue'}); + return; + } + + //decrease product available count + product.count = parseInt(product.count) - 1; + //extreme case + if (product.count < 0) { + product.count = 0; + } + try{ + let trans = await db.collection('products').save(product); + console.log('success updating product count'); + }catch(err){ + console.log(err); + } + + + res.send({'status': 'Success'}); +}); + /* end point for tracking gadget buy orders */ app.get('/buyGadget/:user/:gadget/:blockNo/:trxID/:bchain', async function (req, res) { @@ -3356,6 +3499,104 @@ app.get('/proceedAfitTransition', async function(req,res){ } }); + +/* function handles the processing of a buy order paid in HIVE */ +app.get('/processBuyOrderHive', async function(req, res){ + if (!req.query.user || !req.query.product_id) { + //make sure all params are sent + res.send({'error':'generic error'}); + }else{ + let user = req.query.user; + let product_id = req.query.product_id; + //confirm matching funds password + let query = {user: user}; + + let access_token; + + //fetch product info + let product = await grabProductInfo (product_id); + if (!product){ + res.send({'error': 'Product not found'}); + return; + } + + let price_options = product.price; + let price_options_count = price_options.length; + let item_price = 0; + let item_currency = 'HIVE'; + let actifit_percent_cut = 10; + for (let i=0; i < price_options_count; i++){ + let entry = price_options[i]; + item_price = entry.price; + item_currency = entry.currency; + actifit_percent_cut = entry.actifit_percent_cut; + } + + + //product.provider = 'actifit.test.provider'; + + //perform transaction + let productBuyTrans = { + user: user, + reward_activity: 'Buy Product', + buyer: user, + seller: product.provider, + product_id: product_id, + product_type: product.type, + product_price: item_price, + token_count: -item_price, + note: 'Bought Product '+product.name+ ' by '+product.provider, + date: new Date(), + } + try{ + console.log(productBuyTrans); + let transaction = await db.collection('token_transactions').insert(productBuyTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing buy action. DB storing issue'}); + return; + } + + + + //store this in escrow + let productSellTrans = { + user: config.escrow_account,//targetAccount,//product.provider,//config.escrow_account, + reward_activity: 'Sell Product', + buyer: user, + seller: product.provider, + product_id: product_id, + product_type: product.type, + product_price: item_price, + token_count: item_price, + actifit_percent_cut: actifit_percent_cut, + note: 'Sold Product '+product.name+ ' to '+user, + date: new Date(), + } + + //alternatively, send to provider directly + if (product.type == 'ebook'){ + //close the transaction on the fly, no need to put in escrow. Rewards goes to seller + productSellTrans.user = product.provider; + productSellTrans.token_count = parseFloat(item_price) * (100 - parseFloat(actifit_percent_cut)) / 100; + } + + try{ + console.log(productSellTrans); + let transaction = await db.collection('gadget_transactions_hive').insert(productSellTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing sell action. DB storing issue'}); + return; + } + + res.send({'status': 'Success', 'access_token': access_token}); + } +}) + + /* function handles the processing of a buy order */ app.get('/processBuyOrder', async function(req, res){ if (!req.query.user || !req.query.product_id) { diff --git a/utils.js b/utils.js index 9b53082..a435fb3 100644 --- a/utils.js +++ b/utils.js @@ -541,28 +541,6 @@ var HOURS = 60 * 60; console.log('account available'); - //create keys for new account - const ownerKey = dhive.PrivateKey.fromLogin(username, password, 'owner'); - const activeKey = dhive.PrivateKey.fromLogin(username, password, 'active'); - const postingKey = dhive.PrivateKey.fromLogin(username, password, 'posting'); - let memoKey = dhive.PrivateKey.fromLogin(username, password, 'memo').createPublic(); - - //create auth values for passing to account creation - const ownerAuth = { - weight_threshold: 1, - account_auths: [], - key_auths: [[ownerKey.createPublic(), 1]], - }; - const activeAuth = { - weight_threshold: 1, - account_auths: [], - key_auths: [[activeKey.createPublic(), 1]], - }; - const postingAuth = { - weight_threshold: 1, - account_auths: [], - key_auths: [[postingKey.createPublic(), 1]], - }; //container for required ops let ops = []; @@ -575,6 +553,31 @@ var HOURS = 60 * 60; let hiveAccountSuccess = false; if (!chain || chain == 'STEEM'){ + + //create keys for new account + const ownerKey = dsteem.PrivateKey.fromLogin(username, password, 'owner'); + const activeKey = dsteem.PrivateKey.fromLogin(username, password, 'active'); + const postingKey = dsteem.PrivateKey.fromLogin(username, password, 'posting'); + let memoKey = dsteem.PrivateKey.fromLogin(username, password, 'memo').createPublic(); + + //create auth values for passing to account creation + const ownerAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[ownerKey.createPublic(), 1]], + }; + const activeAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[activeKey.createPublic(), 1]], + }; + const postingAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[postingKey.createPublic(), 1]], + }; + + const _creator_account = await client.database.call('get_accounts', [ [creator], ]); @@ -635,7 +638,30 @@ var HOURS = 60 * 60; ]); console.log('current pending claimed accounts: ' + _creator_account[0].pending_claimed_accounts); - if (_creator_account[0].pending_claimed_accounts > 0) { + //create keys for new account + const ownerKey = dhive.PrivateKey.fromLogin(username, password, 'owner'); + const activeKey = dhive.PrivateKey.fromLogin(username, password, 'active'); + const postingKey = dhive.PrivateKey.fromLogin(username, password, 'posting'); + let memoKey = dhive.PrivateKey.fromLogin(username, password, 'memo').createPublic(); + + //create auth values for passing to account creation + const ownerAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[ownerKey.createPublic(), 1]], + }; + const activeAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[activeKey.createPublic(), 1]], + }; + const postingAuth = { + weight_threshold: 1, + account_auths: [], + key_auths: [[postingKey.createPublic(), 1]], + }; + + if (_creator_account[0].pending_claimed_accounts > 0) { //the create discounted account operation const create_op = [ @@ -1371,6 +1397,34 @@ async function rewardPost(post_url, vp, bchain){ return result; } +async function verifyGadgetPayTransaction(userA, gadget_id, item_price, item_price_alt, tx_type, block_num, tx_id, bchain){ + let trx; + console.log('verifyGadgetTransaction'); + try{ + if (bchain == 'STEEM'){ + trx = await client.database.getTransaction({id: tx_id, block_num: block_num}); + }else{ + trx = await hiveClient.database.getTransaction({id: tx_id, block_num: block_num}); + } + console.log(trx); + if (trx && trx.operations + && trx.operations.length > 0){ + console.log(trx.operations[0][1]); + let trx_details = trx.operations[0][1]; + let amnt = trx_details.amount.split(' ')[0];; + //let json_data = JSON.parse(trx_details.json); + console.log(trx_details); + if (trx_details.to == config.full_pay_benef_account && trx_details.memo == tx_type + ':' + gadget_id + && (amnt >= item_price || amnt >= item_price_alt)){ + return {'success': true, 'amount_hive': amnt}; + } + } + }catch(err){ + console.log(err); + } + return false; +} + async function verifyGadgetTransaction(userA, gadget_id, tx_type, block_num, tx_id, bchain){ let trx; console.log('verifyGadgetTransaction'); @@ -1486,5 +1540,6 @@ async function sendNotification(db, user, action_taker, type, details, url){ processSteemTrx: processSteemTrx, sendNotification: sendNotification, confirmAFITXTransition: confirmAFITXTransition, - proceedAfitxMove: proceedAfitxMove + proceedAfitxMove: proceedAfitxMove, + verifyGadgetPayTransaction: verifyGadgetPayTransaction } From 4497aedf87c5418a047caaeab604009466c3a1f4 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Thu, 13 Aug 2020 23:47:33 +0300 Subject: [PATCH 185/193] New Prize System Functionality Implement new prize system functionality support New endpoint for fetching daily user gadgets --- app.js | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.js | 2 +- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index cba2875..4223770 100644 --- a/app.js +++ b/app.js @@ -1419,6 +1419,22 @@ app.get('/buyGadgetHive/:user/:gadget/:blockNo/:trxID/:bchain', async function ( return; } + //add a ticket to the user to enter draw + //perform transaction + let ticketEntry = { + user: user, + product_id: product_id, + product_name: product.name, + product_level: product.level, + product_price_afit: item_price_afit, + product_price_hive: item_price, + hive_paid: ver_trx.amount_hive, + currency: req.params.bchain, + count: 1, + date: new Date(), + } + let transaction = await db.collection('gadget_buy_tickets').insert(ticketEntry); + //store into user_gadgets table as well let userGadgetTrans = { user: user, @@ -1462,6 +1478,37 @@ app.get('/buyGadgetHive/:user/:gadget/:blockNo/:trxID/:bchain', async function ( res.send({'status': 'Success'}); }); +//end point for fetching a user's active buy gadget tickets during draw period +app.get('/userActiveGadgetBuyTickets/:user', async function (req, res) { + let startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + + let endDate = moment(moment(startDate).utc().subtract(config.contestBuyLen, 'days').toDate()).format('YYYY-MM-DD'); + + console.log("startDate:"+startDate+" endDate:"+endDate); + + let result = await db.collection('gadget_buy_tickets').aggregate([ + {$match: + { + user: req.params.user, + date: { + $gt: new Date(endDate), + //$lte: new Date(startDate) + }, + }, + }, + {$group: + { + _id: null, + tickets_collected: { $sum: "$count" }, + entries: { $sum: 1 } + } + } + ]).toArray(); + + res.send(result); + +}); + /* end point for tracking gadget buy orders */ app.get('/buyGadget/:user/:gadget/:blockNo/:trxID/:bchain', async function (req, res) { @@ -3832,6 +3879,31 @@ app.get("/gadgetsBought", async function(req, res){ res.send(gadgets); }); +app.get("/gadgetsBoughtByDate", async function(req, res){ + let startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + if (req.query.targetDate){ + startDate = moment(moment(req.query.targetDate).utc().startOf('date').toDate()).format('YYYY-MM-DD'); + } + let endDate = moment(moment(startDate).utc().subtract(1, 'days').toDate()).format('YYYY-MM-DD'); + let gadgets = await db.collection('user_gadgets').find( + { + date_bought:{ + $lte: new Date(startDate), + $gt: new Date(endDate) + } + }).toArray(); + let usersArray = []; + for (let i=0;i= item_price || amnt >= item_price_alt)){ return {'success': true, 'amount_hive': amnt}; } From f81a43c4098bb94f247442d7365ecb813129de20 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Mon, 17 Aug 2020 19:35:02 +0300 Subject: [PATCH 186/193] Implement additional ticket prize support Implement additional ticket prize support --- app.js | 60 +++++++++++++++++++++++++++++++++++++++----------------- utils.js | 40 ++++++++++++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 19 deletions(-) diff --git a/app.js b/app.js index 4223770..3a4977f 100644 --- a/app.js +++ b/app.js @@ -1419,21 +1419,27 @@ app.get('/buyGadgetHive/:user/:gadget/:blockNo/:trxID/:bchain', async function ( return; } - //add a ticket to the user to enter draw - //perform transaction - let ticketEntry = { - user: user, - product_id: product_id, - product_name: product.name, - product_level: product.level, - product_price_afit: item_price_afit, - product_price_hive: item_price, - hive_paid: ver_trx.amount_hive, - currency: req.params.bchain, - count: 1, - date: new Date(), + //add a ticket to the user to enter draw if user meets min requirements + let user_info = await grabUserTokensFunc (user); + console.log(user_info); + let cur_user_token_count = parseFloat(user_info.tokens); + + if (cur_user_token_count >= config.minUserTokensGadgetTicket){ + //perform transaction + let ticketEntry = { + user: user, + product_id: product_id, + product_name: product.name, + product_level: product.level, + product_price_afit: item_price_afit, + product_price_hive: item_price, + hive_paid: ver_trx.amount_hive, + currency: req.params.bchain, + count: 1, + date: new Date(), + } + let transaction = await db.collection('gadget_buy_tickets').insert(ticketEntry); } - let transaction = await db.collection('gadget_buy_tickets').insert(ticketEntry); //store into user_gadgets table as well let userGadgetTrans = { @@ -1478,20 +1484,38 @@ app.get('/buyGadgetHive/:user/:gadget/:blockNo/:trxID/:bchain', async function ( res.send({'status': 'Success'}); }); + + +//end point for returning latest cycle +app.get("/recentGadgetBuyPrizeCycle", async function(req, res){ + let drawData = await utils.grabLastDrawData(db); + res.send(drawData); +}); + +//end point for fetching all current active entry tickets +app.get('/activeGadgetBuyTickets/', async function (req, res) { + let entries = await utils.getGadgetBuyTickets(db); + res.send(entries); + +}); + //end point for fetching a user's active buy gadget tickets during draw period app.get('/userActiveGadgetBuyTickets/:user', async function (req, res) { - let startDate = moment(moment().utc().startOf('date').toDate()).format('YYYY-MM-DD'); + //fetch last draw date, and start counting tickets since + let drawData = await utils.grabLastDrawData(db); - let endDate = moment(moment(startDate).utc().subtract(config.contestBuyLen, 'days').toDate()).format('YYYY-MM-DD'); + let startDate = moment(drawData.drawDate).format('YYYY-MM-DD'); - console.log("startDate:"+startDate+" endDate:"+endDate); + //let endDate = moment(moment(startDate).utc().subtract(config.contestBuyLen, 'days').toDate()).format('YYYY-MM-DD'); + + console.log("startDate:"+startDate);//+" endDate:"+endDate); let result = await db.collection('gadget_buy_tickets').aggregate([ {$match: { user: req.params.user, date: { - $gt: new Date(endDate), + $gte: new Date(startDate), //$lte: new Date(startDate) }, }, diff --git a/utils.js b/utils.js index 8fb39fd..a2889a8 100644 --- a/utils.js +++ b/utils.js @@ -1498,6 +1498,42 @@ async function sendNotification(db, user, action_taker, type, details, url){ } } + +async function grabLastDrawData(db){ + let lastDraw = await db.collection('gadget_buy_prize_draw').find().sort({'drawDate': -1}).toArray(); + let drawData = {}; + if (Array.isArray(lastDraw) && lastDraw.length > 0){ + let start = moment(lastDraw[0].drawDate).utc().startOf('date').toDate(); + let nextDrawDate = moment(start).add(config.contestBuyLen, 'days').toDate(); + lastDraw[0].nextDrawDate = nextDrawDate; + drawData = lastDraw[0]; + }else{ + let start = moment(config.gadgetPrizeInitDate).utc().startOf('date').toDate(); + let nextDrawDate = moment(start).add(config.contestBuyLen, 'days').toDate(); + drawData = {'drawDate': start, 'nextDrawDate': nextDrawDate}; + } + return drawData; +} + +async function getGadgetBuyTickets(db){ + let drawData = await grabLastDrawData(db); + + let startDate = moment(drawData.drawDate).format('YYYY-MM-DD'); + + //let endDate = moment(moment(startDate).utc().subtract(config.contestBuyLen, 'days').toDate()).format('YYYY-MM-DD'); + + console.log("startDate:"+startDate);//+" endDate:"+endDate); + + let query = { + date: { + $gte: new Date(startDate), + } + } + + let result = await db.collection('gadget_buy_tickets').find(query).toArray(); + return result; +} + module.exports = { updateSteemVariables: updateSteemVariables, getVotingPower: getVotingPower, @@ -1541,5 +1577,7 @@ async function sendNotification(db, user, action_taker, type, details, url){ sendNotification: sendNotification, confirmAFITXTransition: confirmAFITXTransition, proceedAfitxMove: proceedAfitxMove, - verifyGadgetPayTransaction: verifyGadgetPayTransaction + verifyGadgetPayTransaction: verifyGadgetPayTransaction, + getGadgetBuyTickets: getGadgetBuyTickets, + grabLastDrawData: grabLastDrawData } From ccf05b8d46759dd6b82fd731e82a92b3aee5b3c5 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Wed, 19 Aug 2020 23:42:31 +0300 Subject: [PATCH 187/193] Implement contest improvements + Multi gadget - Improve script for rewarding contest winners - Fix bugs in bulk activating gadgets --- app.js | 386 +++++++++++++++++++++++++++++++++++++++++++++++++ delegations.js | 129 ++++++++++++++++- 2 files changed, 514 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 3a4977f..8e4fdc3 100644 --- a/app.js +++ b/app.js @@ -1361,6 +1361,7 @@ app.get('/buyGadgetHive/:user/:gadget/:blockNo/:trxID/:bchain', async function ( let item_price_afit = 0; let item_currency = req.params.bchain; let actifit_percent_cut = 10; + let item_price_alt = 0; for (let i=0; i < price_options_count; i++){ let entry = price_options[i]; //calculate HIVE price @@ -1485,6 +1486,194 @@ app.get('/buyGadgetHive/:user/:gadget/:blockNo/:trxID/:bchain', async function ( }); +/* end point for tracking multi-gadget buy orders */ +app.get('/buyMultiGadgetHive/:user/:gadgets/:blockNo/:trxID/:bchain', async function (req, res) { + + let user = req.params.user; + let product_ids = req.params.gadgets.split('-'); + + let products_tot_price_afit = 0; + let products_tot_hive_price = 0; + let products_tot_hive_price_alt = 0; + for (let i=0;i 0){ + res.send({'error': 'Transaction already verified'}); + return; + } + + + //round down number + console.log('Before rounding'); + console.log(products_tot_hive_price); + products_tot_hive_price = (Math.floor(products_tot_hive_price * 1000) - 1) / 1000; + console.log('After rounding'); + console.log(products_tot_hive_price); + + //ensure proper transaction + let ver_trx = await utils.verifyGadgetPayTransaction(req.params.user, req.params.gadgets, products_tot_hive_price, products_tot_hive_price_alt, 'buy-gadget', req.params.blockNo, req.params.trxID, req.params.bchain); + if (!ver_trx || !ver_trx.success){ + res.send({status: 'error'}); + return; + } + + + let provider = 'actifit'; + + //perform transaction + let productBuyTrans = { + user: user, + reward_activity: 'Buy Product', + buyer: user, + seller: provider, + product_ids: product_ids, + product_price_afit: products_tot_price_afit, + product_price_hive_tot: products_tot_hive_price, + hive_paid: ver_trx.amount_hive, + currency: req.params.bchain, + blockNo: req.params.blockNo, + trxID: req.params.trxID, + bchain: req.params.bchain, + note: 'Bought Products '+req.params.gadgets, + date: new Date(), + } + try{ + console.log(productBuyTrans); + let transaction = await db.collection('gadget_transactions_hive').insert(productBuyTrans); + console.log('success inserting post data'); + }catch(err){ + console.log(err); + res.send({'error': 'Error performing buy action. DB storing issue'}); + return; + } + + //add a ticket to the user to enter draw if user meets min requirements + let user_info = await grabUserTokensFunc (user); + console.log(user_info); + let cur_user_token_count = parseFloat(user_info.tokens); + + if (cur_user_token_count >= config.minUserTokensGadgetTicket){ + for (let i=0;i 0){ + lastDrawDate = lastDraw[0].drawDate; + } + let today = moment().utc().startOf('date').toDate() + let start = moment(lastDrawDate).utc().startOf('date').toDate() + let nextDrawDate = moment(start).add(config.contestBuyLen, 'days').toDate() + //let nextDrawDate = lastDrawDate+ + console.log(today); + console.log(nextDrawDate); + console.log((today.getTime() == nextDrawDate.getTime())); + //check if this is the proper date to kick off reward + if (today.getTime() == nextDrawDate.getTime()){ + console.log('kick off draw reward'); + //fetch list of ticket holders + let entries = await utils.getGadgetBuyTickets(db); + console.log(entries); + + //randomly pick winner and send rewards + if (Array.isArray(entries) && entries.length > 0){ + + //fetch reward pool + hive.api.getAccounts([config.gadget_buy_account], async function(err, response){ + //console.log(err, response); + if (!err){ + let prizePool = response[0].balance; + let prizePoolValue = parseFloat(prizePool.split(' ')[0]) * config.userRewardPrizePercent / 100; + + console.log('prize pool value: '+prizePoolValue); + + //pick winner + let lucky_winner_id = utils.generateRandomNumber(1, entries.length); + let winner_name = entries[lucky_winner_id].user; + console.log('Winner is ......'+winner_name); + let currency = 'HIVE'; + let memo= 'Congrats on winning Actifit random gadget purchase prize!'; + + //reward winner + + let res = await hive.broadcast.transferAsync(config.gadget_buy_account_ak, config.gadget_buy_account, winner_name, parseFloat(prizePoolValue).toFixed(3) + ' ' + currency, memo);/*.then( + res => { + //store last draw results + let drawInfo = { + drawDate: new Date(); + winner: [{name: winner_name, position: 1, reward: prizePoolValue, currency: currency}], + rewardPool: prizePoolValue, + participatingTickets: entries + } + db.collection('gadget_buy_prize_draw').insert(drawInfo); + }).catch(err=>console.log(err));*/ + + let buyBackAmount = parseFloat(prizePool.split(' ')[0]) * config.buyBackPrizePercent / 100; + + let projectSupportAmount = parseFloat(prizePool.split(' ')[0]) * config.actifitFundPrizePercent / 100; + console.log(res); + if (res){ + //store last draw results + let drawInfo = { + drawDate: new Date(), + winner: [{name: winner_name, position: 1, reward: prizePoolValue, currency: currency, buyback: buyBackAmount, projectSupportAmount: projectSupportAmount}], + rewardPool: prizePoolValue, + participatingTickets: entries + } + db.collection('gadget_buy_prize_draw').insert(drawInfo); + } + + memo= 'Funds to buy back from Actifit random gadget purchase prize!'; + + //send 25% of funds to buy back tokens + + res = await hive.broadcast.transferAsync(config.gadget_buy_account_ak, config.gadget_buy_account, config.buy_account, parseFloat(buyBackAmount).toFixed(3) + ' ' + currency, memo); + + console.log(res); + + //keep 25% of funds to actifit project + memo= 'Funds to keep as 25% based on prize results.'; + + + res = await hive.broadcast.transferAsync(config.gadget_buy_account_ak, config.gadget_buy_account, config.full_pay_benef_account, parseFloat(projectSupportAmount).toFixed(3) + ' ' + currency, memo); + + console.log(res); + + } + }); + + + + }else{ + console.log('no ticket entries to reward'); + } + + } + + + + } + }); +} + async function testMove(){ From 2ffddc4f0330fb84acb608a0bde136166a853c69 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 22 Aug 2020 23:44:21 +0300 Subject: [PATCH 188/193] Endpoint for aggregated ticket data Create new endpoint for aggregated ticket data grouped by user and total count --- app.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/app.js b/app.js index 8e4fdc3..782354b 100644 --- a/app.js +++ b/app.js @@ -1688,6 +1688,39 @@ app.get('/activeGadgetBuyTickets/', async function (req, res) { }); + +//end point for fetching a user's active buy gadget tickets during draw period +app.get('/userActiveGadgetBuyTicketsByUser/', async function (req, res) { + //fetch last draw date, and start counting tickets since + let drawData = await utils.grabLastDrawData(db); + + let startDate = moment(drawData.drawDate).format('YYYY-MM-DD'); + + + console.log("startDate:"+startDate);//+" endDate:"+endDate); + + let result = await db.collection('gadget_buy_tickets').aggregate([ + {$match: + { + date: { + $gte: new Date(startDate), + //$lte: new Date(startDate) + }, + }, + }, + {$group: + { + _id: '$user', + tickets_collected: { $sum: "$count" }, + /*entries: { $sum: 1 }*/ + } + } + ]).toArray(); + + res.send({"userCount": result.length, "result": result}); + +}); + //end point for fetching a user's active buy gadget tickets during draw period app.get('/userActiveGadgetBuyTickets/:user', async function (req, res) { //fetch last draw date, and start counting tickets since From 95fb24eaaf965eca45f9aafc6159dbdc1bdbc0df Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Sat, 22 Aug 2020 23:47:05 +0300 Subject: [PATCH 189/193] Add notification upon winning prize Notify user on actifit.io when winning a prize --- delegations.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/delegations.js b/delegations.js index 7851419..3d5ced2 100644 --- a/delegations.js +++ b/delegations.js @@ -191,6 +191,9 @@ async function processGadgetBuyPrize() { console.log(res); + //send notification to the user about winning + utils.sendNotification(db, winner_name, 'actifit', 'prize_pool_draw_winner', 'Congratulations! You have won the prize pool of the gadget buy contest! ' + prizePoolValue + ' HIVE have been sent to your wallet', 'https://actifit.io/'+winner_name); + } }); From f822b81f2046d4a3f3a733efea952c886b2e5399 Mon Sep 17 00:00:00 2001 From: Mohammad Farhat Date: Tue, 25 Aug 2020 18:38:26 +0300 Subject: [PATCH 190/193] Add ticket count to API endpoint Add ticket count to API endpoint --- app.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app.js b/app.js index 782354b..1ad306c 100644 --- a/app.js +++ b/app.js @@ -1716,8 +1716,11 @@ app.get('/userActiveGadgetBuyTicketsByUser/', async function (req, res) { } } ]).toArray(); - - res.send({"userCount": result.length, "result": result}); + let ticketCount = 0; + for (let i=0;i Date: Thu, 27 Aug 2020 23:02:59 +0300 Subject: [PATCH 191/193] Fix multi gadget activation + ticket notifications Fix issue with multi gadget activation New notification type upon earning tickets for gadget purchase --- app.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 1ad306c..c079069 100644 --- a/app.js +++ b/app.js @@ -1625,6 +1625,9 @@ app.get('/buyMultiGadgetHive/:user/:gadgets/:blockNo/:trxID/:bchain', async func date: new Date(), } let transaction = await db.collection('gadget_buy_tickets').insert(ticketEntry); + + //insert notification to user about new ticket + utils.sendNotification(db, user, 'actifit', 'ticket_collected', 'You collected a ticket for purchasing gadget "' + product.name + ' - L'+ product.level + '" to enter Actifit Gadget Prize Draw!', 'https://actifit.io/'+user); } } @@ -4422,7 +4425,7 @@ app.get('/activateMultiGadget/:user/:gadgets/:blockNo/:trxID/:bchain/:benefic?', } let gadget_entries = req.params.gadgets.split('-'); - + let err = ''; for (let i=0;i Date: Tue, 8 Sep 2020 13:23:55 +0300 Subject: [PATCH 192/193] Support friend post notifications Append support for friend creating post notifications --- app.js | 70 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/app.js b/app.js index c079069..7f153d2 100644 --- a/app.js +++ b/app.js @@ -732,6 +732,10 @@ app.post('/loginAuth', async function (req, res) { } }); +app.get('/getDailyDelegationPool/', async function(req, res){ + res.send({'hive_pool': config.weekly_rewards_limit, 'steem_pool': config.weekly_rewards_limit}); +}); + /* end point for user total token count display */ app.get('/user/:user', async function (req, res) { let user = await grabUserTokensFunc(req.params.user); @@ -767,17 +771,28 @@ app.get('/transactionsByType/', async function (req, res) { let transactions = {}; let proceed = false; let dateSort = 1; + let sortQuery = {}; if (req.query.type){ proceed = true; query = {reward_activity: req.query.type} } + if (req.query.chain){ + query['chain'] = req.query.chain; + } if (req.query.datesort){ dateSort = parseInt(req.query.datesort) - - } - let startDate = ''; - let endDate = ''; + sortQuery = {date: dateSort}; + }else if (req.query.sortByToken && !isNaN(req.query.sortByToken)){ + sortQuery = {token_count: parseInt(req.query.sortByToken)}; + } + console.log(sortQuery); + //default end date as yesterday + let endDate = moment(moment().utc().subtract(1, 'days').toDate()).format('YYYY-MM-DD'); + //default start date as day before + let startDate = moment(moment(endDate).utc().subtract(1, 'days').toDate()).format('YYYY-MM-DD');; + console.log('startDate:'+startDate); + console.log('endDate:'+endDate); if (req.query.startDate){ startDate = moment(moment(req.query.startDate).utc().startOf('date').add(1, 'days').toDate()).format('YYYY-MM-DD'); } @@ -786,22 +801,23 @@ app.get('/transactionsByType/', async function (req, res) { endDate = moment(moment(req.query.endDate).utc().endOf('date').add(1, 'days').toDate()).format('YYYY-MM-DD'); } if (startDate && endDate){ - query["date"] = { + query['date'] = { "$lt": new Date(endDate), "$gte": new Date(startDate) }; }else if (startDate){ - query["date"] = { + query['date'] = { "$gte": new Date(startDate) }; } console.log(query); if (proceed){ - transactions = await db.collection('token_transactions').find(query).sort({date: dateSort}).limit(1000).toArray(); + transactions = await db.collection('token_transactions').find(query).sort(sortQuery).limit(1000).toArray(); } res.send(transactions); }); + /* end point for user signup display (per user or general signups */ app.get('/signups/:user?', async function (req, res) { let query = {account_created: true}; @@ -1270,15 +1286,20 @@ app.get('/userBadges/:user', async function (req, res) { res.send(user); }); -/* end point for fetching user's friends */ -app.get('/userFriends/:user', async function (req, res) { - let friendsA = await db.collection('friends').find({userA: req.params.user}, {fields : {userB:1, _id:0}}).toArray(); - let friendsB = await db.collection('friends').find({userB: req.params.user}, {fields : {userA:1, _id:0}}).toArray(); +async function getUserFriends(user){ + let friendsA = await db.collection('friends').find({userA: user}, {fields : {userB:1, _id:0}}).toArray(); + let friendsB = await db.collection('friends').find({userB: user}, {fields : {userA:1, _id:0}}).toArray(); console.log(friendsA); console.log(friendsB); friendsA = JSON.parse(JSON.stringify(friendsA).replace(/userB/g,'friend')); friendsB = JSON.parse(JSON.stringify(friendsB).replace(/userA/g,'friend')); - res.send(friendsA.concat(friendsB)); + return friendsA.concat(friendsB); +} + +/* end point for fetching user's friends */ +app.get('/userFriends/:user', async function (req, res) { + let friendList = await getUserFriends(req.params.user); + res.send(friendList); }); /* end point for marking a notification as read */ @@ -4767,6 +4788,31 @@ claimAndCreateAccount = async function (req){ }; +//send notification +app.get('/sendNotification', async function(req,res){ + let passed_var = eval("req.query."+config.verifyNotifParam); + //console.log(passed_var); + //make sure needed security var is passed, and with proper value + if ((typeof passed_var == 'undefined') || passed_var != config.verifyNotifToken){ + res.send('{}'); + }else{ + if (req.query.notifType == 'new_post'){ + //first notify post owner + utils.sendNotification(db, req.query.user, req.query.actionTaker, req.query.notifType, 'You successfully created a new actifit report "' + req.query.title + '" ', 'https://actifit.io/'+req.query.user+'/'+req.query.permlink); + + //fetch user friends + let friends = await getUserFriends(req.query.user); + //send out a notification for each friend + for (let i=0;i Date: Thu, 10 Dec 2020 21:29:54 +0000 Subject: [PATCH 193/193] Bump ini from 1.3.5 to 1.3.7 Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7. - [Release notes](https://github.com/isaacs/ini/releases) - [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7) Signed-off-by: dependabot[bot] --- package-lock.json | 66 ++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a43a06..8da437e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -577,6 +577,17 @@ "to-object-path": "^0.3.0", "union-value": "^1.0.0", "unset-value": "^1.0.0" + }, + "dependencies": { + "set-value": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-3.0.2.tgz", + "integrity": "sha512-npjkVoz+ank0zjlV9F47Fdbjfj/PfXyVhZvGALWsyIYU/qrMzpi6avjKW3/7KeSU2Df3I46BrN1xOI1+6vW0hA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } } }, "capture-stack-trace": { @@ -741,6 +752,17 @@ "unique-string": "^1.0.0", "write-file-atomic": "^2.0.0", "xdg-basedir": "^3.0.0" + }, + "dependencies": { + "dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + } } }, "content-disposition": { @@ -1006,15 +1028,6 @@ "domelementtype": "1" } }, - "dot-prop": { - "version": ">=5.1.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, "drbg.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", @@ -1680,12 +1693,6 @@ "dev": true, "optional": true }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, @@ -2323,9 +2330,9 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz", + "integrity": "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==", "dev": true }, "ipaddr.js": { @@ -3637,29 +3644,6 @@ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", "dev": true }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",