Project Update: we-resist-bot - New Voting and Comment Queues

in utopian-io •  7 years ago  (edited)

Voting Queues

It used to be that our process for voting was just vote everyone simultaneously. This causes problems when multiple votes happen at once for each voter. Votes end up getting pseudo-queued or delayed. This has been working for awhile, but it's starting to become difficult to maintain.

The solution is to vote entirely by queue. Items are placed on the queue as quickly as possible. They are then pulled off the queue at constrained intervals allowing votes to happen more organically.

Comment Queues

Same as with voting, comments were getting thrust out as fast as possible. Unlike votes, comments are made for a the bot user only; however, they happen in the same promise as votes. It just makes more sense to add comments to a queue, so they can go out organically like the votes. The queue isn't completely necessary, but it makes the process cleaner and easier to maintain.

Updated Templates

Comments/replies were being consistently misunderstood. Templates were rewritten to be more clear.

Changes

diff --git a/app/helpers/bot/comment.js b/app/helpers/bot/comment.js
new file mode 100644
index 0000000..866f395
--- /dev/null
+++ b/app/helpers/bot/comment.js
@@ -0,0 +1,73 @@
+const Promise = require('bluebird')
+const steem = require('steem')
+const sc2 = Promise.promisifyAll(require('sc2-sdk'))
+const { user, wif, sc2_secret, steemit_url, grumpy, blacklisted } = require('../../config')
+const schedule = require('node-schedule')
+const Sequelize = require('sequelize')
+const Handlebars = require('handlebars')
+const fs = Promise.promisifyAll(require('fs'))
+const path = require('path')
+
+steem.api.setWebSocket(steemit_url)
+
+const MINUTE = new schedule.RecurrenceRule();
+MINUTE.second = 1
+
+function loadTemplate(template) {
+    return fs.readFileAsync(template, 'utf8')
+}
+
+
+function execute(comments) {
+    schedule.scheduleJob(MINUTE, function() {
+        const api = sc2.Initialize({
+            app: 'we-resist',
+            callbackURL: 'https://we-resist-bot.herokuapp.com/',
+            accessToken: '',
+            scope: ['vote', 'comment', 'offline']
+        })
+
+        if (comments.length() < 1) {
+            return {};
+        }
+
+        const { author, permlink, type } = comments.shift();
+
+        var context = {
+        }
+    
+        return loadTemplate(path.join(__dirname, '..', 'templates', `${type}.hb`))
+            .then((template) => {
+                var templateSpec = Handlebars.compile(template)
+                return templateSpec(context)
+            })
+            .then((message) => {
+                var new_permlink = 're-' + author 
+                    + '-' + permlink 
+                    + '-' + new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, '').toLowerCase();
+                console.log("Commenting on ", author, permlink, type)
+
+                return steem.broadcast.commentAsync(
+                    wif,
+                    author, // Leave parent author empty
+                    permlink, // Main tag
+                    user, // Author
+                    new_permlink, // Permlink
+                    new_permlink,
+                    message, // Body
+                    { tags: [], app: 'we-resist-bot/0.1.0' }
+                ).then((results) => {
+                    console.log(results)
+                    return results
+                })
+                .catch((err) => {
+                    console.log("Error ", err.message)
+                })
+            })
+    })
+}
+
+module.exports = {
+    execute
+}

This is the new comment module that pulls comments off the queue and
manipulates them in the blockchain.

diff --git a/app/helpers/bot/index.js b/app/helpers/bot/index.js
index d6b9ee8..dffc00f 100644
--- a/app/helpers/bot/index.js
+++ b/app/helpers/bot/index.js
@@ -1,12 +1,36 @@
 'use strict'
 
-const scheduler = require('node-schedule')
-
 
 module.exports = {
     run
 }
 
+const voting_queue = [];
+const comment_queue = [];
+
+const voting = {
+    length: () => { return voting_queue.length },
+    push: (obj) => { return voting_queue.push(obj) },
+    pop: () => { return voting_queue.pop() },
+    shift: () => { return voting_queue.shift() },
+    unshift: (obj) => { return voting_queue.unshift(obj) }
+}
+
+const comments = {
+    length: () => { return comment_queue.length },
+    push: (obj) => { 
+        return comment_queue.push(obj) 
+    },
+    pop: () => { return comment_queue.pop() },
+    shift: () => {
+        console.log("Shifting comments ", comment_queue)
+        return comment_queue.shift() 
+    },
+    unshift: (obj) => { return comment_queue.unshift(obj) }
+}
+
 function run() {
-    require('./task').execute()
+    require('./vote').execute(voting);
+    require('./comment').execute(comments);
+    require('./task').execute(voting, comments);
 }

This where voting and comment queues live. They are passed into
modules for voting, commenting, and the main bot task.

diff --git a/app/helpers/bot/task.js b/app/helpers/bot/task.js
index 26076b0..b8f8766 100644
--- a/app/helpers/bot/task.js
+++ b/app/helpers/bot/task.js
@@ -21,6 +21,9 @@ module.exports = {
     execute
 }
 
+let VOTING = {};
+let COMMENTS = {};
+
 const SECONDS_PER_HOUR = 3600
 const PERCENT_PER_DAY = 20
 const HOURS_PER_DAY = 24
@@ -47,10 +50,6 @@ function time_needed_to_recover(voting_power, threshold) {
     return (threshold - voting_power) / RECOVERY_RATE
 }
 
-function loadTemplate(template) {
-    return fs.readFileAsync(template, 'utf8')
-}
-
 // Stubbed function
 function list_of_grumpy_users() {
     let grumps = []
@@ -131,20 +130,6 @@ function list_of_resisters() {
     })
 }
 
-function fetch_access_token(resister) {
-    return rp({
-        method: "POST",
-        uri: "https://v2.steemconnect.com/api/oauth2/token",
-        body: {
-          refresh_token: resister.refresh_token,
-          client_id: "we-resist",
-          client_secret: sc2_secret,
-          scope: "vote,comment,offline"
-        },
-        json: true
-      })
-}
-
 function processDownvote(vote) {
     console.log('Processing vote ', vote)
     return collectiveUpvote(vote.author, vote.permlink)
@@ -192,148 +177,38 @@ function is_already_replied_to(author, permlink) {
 
 
 function reply(author, permlink, type) {
-    var context = {
-    }
-
-    return loadTemplate(path.join(__dirname, '..', 'templates', `${type}.hb`))
-        .then((template) => {
-            var templateSpec = Handlebars.compile(template)
-            return templateSpec(context)
-        })
-        .then((message) => {
-            var new_permlink = 're-' + author 
-                + '-' + permlink 
-                + '-' + new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, '').toLowerCase();
-            steem.broadcast.commentAsync(
-                wif,
-                author, // Leave parent author empty
-                permlink, // Main tag
-                user, // Author
-                new_permlink, // Permlink
-                new_permlink,
-                message, // Body
-                { tags: [], app: 'we-resist-bot/0.1.0' }
-            ).then((results) => {
-                console.log(results)
-            })
-        })
+    COMMENTS.push({ author: author, permlink: permlink, type: type })
 }
 
 function downvote(author, permlink, resister) {
-    var recovery_wait = 0
-    return steem.api.getAccountsAsync([ resister.username ]).then((accounts) => {
-        if (accounts && accounts.length > 0) {
-            const account = accounts[0];
-            console.log("Getting voting power for %d %s", account.voting_power, account.last_vote_time)
-            var voting_power = current_voting_power(account.voting_power, account.last_vote_time)
-            recovery_wait = time_needed_to_recover(voting_power, DEFAULT_THRESHOLD) / 60
-            return account
-        }
-    })
-    .then((account) => {
-        // Reschedule vote
-        if (recovery_wait > 0) {
-            console.log("Rescheduling ", recovery_wait, " minutes to recover")
-            var later = moment().add(recovery_wait, 'minutes').toDate()
-            schedule.scheduleJob(later, function() {
-                downvote(author, permlink, resister)
-            })
-            return account
-        }
-        return vote(author, permlink, resister, resister.downvoteWeight * -100)
-            .then((promise) => { 
-                return is_already_replied_to(author, permlink) 
-                    .then((found) => { 
-                        if (!found) {
-                            return reply(author, permlink, "downvote") 
-                        }
-                        return found;
-                    });
-            });
-            
-    })
-    return vote(author, permlink, resister, resister.downvoteWeight * -100)
+    vote(author, permlink, resister, resister.downvoteWeight * -100)
+    return is_already_replied_to(author, permlink) 
+        .then((found) => { 
+            if (!found) {
+                return reply(author, permlink, "downvote") 
+            }   
+            return found;
+        });
 }
 
 function upvote(author, permlink, resister) {
     var recovery_wait = 0
-    return steem.api.getAccountsAsync([ resister.username ]).then((accounts) => {
-        if (accounts && accounts.length > 0) {
-            const account = accounts[0];
-            console.log("Getting voting power for %d %s", account.voting_power, account.last_vote_time)
-            var voting_power = current_voting_power(account.voting_power, account.last_vote_time)
-            recovery_wait = time_needed_to_recover(voting_power, DEFAULT_THRESHOLD) / 60
-            return account
-        }
-    })
-    .then((account) => {
-        // Reschedule vote
-        if (recovery_wait > 0) {
-            console.log("Rescheduling ", recovery_wait, " minutes to recover")
-            var later = moment().add(recovery_wait, 'minutes').toDate()
-            schedule.scheduleJob(later, function() {
-                upvote(author, permlink, resister)
-            })
-            return account
-        }
-        return vote(author, permlink, resister, resister.upvoteWeight * 100)
-            .then((promise) => {
-                return is_already_replied_to(author, permlink)
-                    .then((found) => {
-                        if (!found) { // we we haven't replied yet
-                            return reply(author, permlink, "upvote") 
-                        }
-                        return found;
-                    });
-            });
-    })
+    vote(author, permlink, resister, resister.upvoteWeight * 100)
+    return is_already_replied_to(author, permlink)
+        .then((found) => {
+            if (!found) { // we we haven't replied yet
+                return reply(author, permlink, "upvote") 
+            }
+            return found;
+        });
 }
 
 function unvote(author, permlink, resister) {
-    return vote(author, permlink, resister, UNVOTE_WEIGHT)
+    vote(author, permlink, resister, UNVOTE_WEIGHT)
 }
 
 function vote(author, permlink, resister, weight) {
-
-    if (resister.refresh_token) {
-        return fetch_access_token(resister)
-            .then((data) => {
-                api.setAccessToken(data.access_token)
-
-                const retval = api.vote(resister.username, 
-                        author,
-                        permlink,
-                        weight,
-                        function(err, results) {
-                            if (err) {
-                                return Promise.reject(err);
-                            }
-                            return Promise.resolve(results);
-                        })
-                api.setAccessToken('')
-                return retval;                
-            })
-            .then((results) => {
-                console.log("Vote result: ", results);
-            })
-            .catch((exception) => {
-                console.log("Unable to vote ", exception)
-            })
-    }
-
-    return steem.broadcast.voteAsync(
-            resister.wif, 
-            resister.username, 
-            author,
-            permlink,
-            weight
-        )
-        .then((results) =>  {
-            console.log(results)
-        })
-        .catch((err) => {
-            console.log("Vote failed: ", err)
-        })
+    VOTING.push({ author: author, permlink: permlink, resister: resister, weight: weight })
 }
 
 function collectiveDownvote(author, permlink) {
@@ -386,16 +261,27 @@ function execute() {
             var operation_name = result[0]
             switch(operation_name) {
                 case 'comment':
-                    // processComment(result[1]);
+                    if (operation.parent_author == '') {
+                        processComment(operation)
+                            .catch((e) => {
+                                console.log("Failed to process comment ", e)
+                            });
+                    }
                     break;
                 case 'vote':
-                    // processVote(new Vote(result[1]))
+                    processVote(new Vote(result[1]))
                     break;
                 case 'unvote':
-                    // processUnvote(new Vote(result[1]))
+                    processUnvote(new Vote(result[1]))
                     break;
                 default:
             }   
         }
     })
+}
+
+function execute(voting_queue, comment_queue) {
+    VOTING = voting_queue;
+    COMMENTS = comment_queue;
+    mainLoop();
 }

Implemented functionality to push votes and comments onto a queue.

diff --git a/app/helpers/bot/vote.js b/app/helpers/bot/vote.js
new file mode 100644
index 0000000..1d6500f
--- /dev/null
+++ b/app/helpers/bot/vote.js
@@ -0,0 +1,144 @@
+const Promise = require('bluebird')
+const steem = require('steem')
+const sc2 = Promise.promisifyAll(require('sc2-sdk'))
+const { user, wif, sc2_secret, steemit_url, grumpy, blacklisted } = require('../../config')
+const schedule = require('node-schedule')
+const Sequelize = require('sequelize')
+const rp = require('request-promise');
+const moment = require('moment');
+
+steem.api.setWebSocket(steemit_url)
+
+const MINUTE = new schedule.RecurrenceRule();
+MINUTE.second = 1
+
+const SECONDS_PER_HOUR = 3600
+const PERCENT_PER_DAY = 20
+const HOURS_PER_DAY = 24
```javascript
diff --git a/app/helpers/bot/comment.js b/app/helpers/bot/comment.js
new file mode 100644
index 0000000..866f395
--- /dev/null
+++ b/app/helpers/bot/comment.js
@@ -0,0 +1,73 @@
+const Promise = require('bluebird')
+const steem = require('steem')
+const sc2 = Promise.promisifyAll(require('sc2-sdk'))
+const { user, wif, sc2_secret, steemit_url, grumpy, blacklisted } = require('../../config')
+const schedule = require('node-schedule')
+const Sequelize = require('sequelize')
+const Handlebars = require('handlebars')
+const fs = Promise.promisifyAll(require('fs'))
+const path = require('path')
+
+steem.api.setWebSocket(steemit_url)
+
+const MINUTE = new schedule.RecurrenceRule();
+MINUTE.second = 1
+
+function loadTemplate(template) {
+    return fs.readFileAsync(template, 'utf8')
+}
+
+
+function execute(comments) {
+    schedule.scheduleJob(MINUTE, function() {
+        const api = sc2.Initialize({
+            app: 'we-resist',
+            callbackURL: 'https://we-resist-bot.herokuapp.com/',
+            accessToken: '',
+            scope: ['vote', 'comment', 'offline']
+        })
+
+        if (comments.length() < 1) {
+            return {};
+        }
+
+        const { author, permlink, type } = comments.shift();
+
+        var context = {
+        }
+    
+        return loadTemplate(path.join(__dirname, '..', 'templates', `${type}.hb`))
+            .then((template) => {
+                var templateSpec = Handlebars.compile(template)
+                return templateSpec(context)
+            })
+            .then((message) => {
+                var new_permlink = 're-' + author 
+                    + '-' + permlink 
+                    + '-' + new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, '').toLowerCase();
+                console.log("Commenting on ", author, permlink, type)
+
+                return steem.broadcast.commentAsync(
+                    wif,
+                    author, // Leave parent author empty
+                    permlink, // Main tag
+                    user, // Author
+                    new_permlink, // Permlink
+                    new_permlink,
+                    message, // Body
+                    { tags: [], app: 'we-resist-bot/0.1.0' }
+                ).then((results) => {
+                    console.log(results)
+                    return results
+                })
+                .catch((err) => {
+                    console.log("Error ", err.message)
+                })
+            })
+    })
+}
+
+module.exports = {
+    execute
+}

This is the new comment module that pulls comments off the queue and
manipulates them in the blockchain.

diff --git a/app/helpers/bot/index.js b/app/helpers/bot/index.js
index d6b9ee8..dffc00f 100644
--- a/app/helpers/bot/index.js
+++ b/app/helpers/bot/index.js
@@ -1,12 +1,36 @@
 'use strict'
 
-const scheduler = require('node-schedule')
-
 
 module.exports = {
     run
 }
 
+const voting_queue = [];
+const comment_queue = [];
+
+const voting = {
+    length: () => { return voting_queue.length },
+    push: (obj) => { return voting_queue.push(obj) },
+    pop: () => { return voting_queue.pop() },
+    shift: () => { return voting_queue.shift() },
+    unshift: (obj) => { return voting_queue.unshift(obj) }
+}
+
+const comments = {
+    length: () => { return comment_queue.length },
+    push: (obj) => { 
+        return comment_queue.push(obj) 
+    },
+    pop: () => { return comment_queue.pop() },
+    shift: () => {
+        console.log("Shifting comments ", comment_queue)
+        return comment_queue.shift() 
+    },
+    unshift: (obj) => { return comment_queue.unshift(obj) }
+}
+
 function run() {
-    require('./task').execute()
+    require('./vote').execute(voting);
+    require('./comment').execute(comments);
+    require('./task').execute(voting, comments);
 }

This where voting and comment queues live. They are passed into
modules for voting, commenting, and the main bot task.

diff --git a/app/helpers/bot/task.js b/app/helpers/bot/task.js
index 26076b0..b8f8766 100644
--- a/app/helpers/bot/task.js
+++ b/app/helpers/bot/task.js
@@ -21,6 +21,9 @@ module.exports = {
     execute
 }
 
+let VOTING = {};
+let COMMENTS = {};
+
 const SECONDS_PER_HOUR = 3600
 const PERCENT_PER_DAY = 20
 const HOURS_PER_DAY = 24
@@ -47,10 +50,6 @@ function time_needed_to_recover(voting_power, threshold) {
     return (threshold - voting_power) / RECOVERY_RATE
 }
 
-function loadTemplate(template) {
-    return fs.readFileAsync(template, 'utf8')
-}
-
 // Stubbed function
 function list_of_grumpy_users() {
     let grumps = []
@@ -131,20 +130,6 @@ function list_of_resisters() {
     })
 }
 
-function fetch_access_token(resister) {
-    return rp({
-        method: "POST",
-        uri: "https://v2.steemconnect.com/api/oauth2/token",
-        body: {
-          refresh_token: resister.refresh_token,
-          client_id: "we-resist",
-          client_secret: sc2_secret,
-          scope: "vote,comment,offline"
-        },
-        json: true
-      })
-}
-
 function processDownvote(vote) {
     console.log('Processing vote ', vote)
     return collectiveUpvote(vote.author, vote.permlink)
@@ -192,148 +177,38 @@ function is_already_replied_to(author, permlink) {
 
 
 function reply(author, permlink, type) {
-    var context = {
-    }
-
-    return loadTemplate(path.join(__dirname, '..', 'templates', `${type}.hb`))
-        .then((template) => {
-            var templateSpec = Handlebars.compile(template)
-            return templateSpec(context)
-        })
-        .then((message) => {
-            var new_permlink = 're-' + author 
-                + '-' + permlink 
-                + '-' + new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, '').toLowerCase();
-            steem.broadcast.commentAsync(
-                wif,
-                author, // Leave parent author empty
-                permlink, // Main tag
-                user, // Author
-                new_permlink, // Permlink
-                new_permlink,
-                message, // Body
-                { tags: [], app: 'we-resist-bot/0.1.0' }
-            ).then((results) => {
-                console.log(results)
-            })
-        })
+    COMMENTS.push({ author: author, permlink: permlink, type: type })
 }
 
 function downvote(author, permlink, resister) {
-    var recovery_wait = 0
-    return steem.api.getAccountsAsync([ resister.username ]).then((accounts) => {
-        if (accounts && accounts.length > 0) {
-            const account = accounts[0];
-            console.log("Getting voting power for %d %s", account.voting_power, account.last_vote_time)
-            var voting_power = current_voting_power(account.voting_power, account.last_vote_time)
-            recovery_wait = time_needed_to_recover(voting_power, DEFAULT_THRESHOLD) / 60
-            return account
-        }
-    })
-    .then((account) => {
-        // Reschedule vote
-        if (recovery_wait > 0) {
-            console.log("Rescheduling ", recovery_wait, " minutes to recover")
-            var later = moment().add(recovery_wait, 'minutes').toDate()
-            schedule.scheduleJob(later, function() {
-                downvote(author, permlink, resister)
-            })
-            return account
-        }
-        return vote(author, permlink, resister, resister.downvoteWeight * -100)
-            .then((promise) => { 
-                return is_already_replied_to(author, permlink) 
-                    .then((found) => { 
-                        if (!found) {
-                            return reply(author, permlink, "downvote") 
-                        }
-                        return found;
-                    });
-            });
-            
-    })
-    return vote(author, permlink, resister, resister.downvoteWeight * -100)
+    vote(author, permlink, resister, resister.downvoteWeight * -100)
+    return is_already_replied_to(author, permlink) 
+        .then((found) => { 
+            if (!found) {
+                return reply(author, permlink, "downvote") 
+            }   
+            return found;
+        });
 }
 
 function upvote(author, permlink, resister) {
     var recovery_wait = 0
-    return steem.api.getAccountsAsync([ resister.username ]).then((accounts) => {
-        if (accounts && accounts.length > 0) {
-            const account = accounts[0];
-            console.log("Getting voting power for %d %s", account.voting_power, account.last_vote_time)
-            var voting_power = current_voting_power(account.voting_power, account.last_vote_time)
-            recovery_wait = time_needed_to_recover(voting_power, DEFAULT_THRESHOLD) / 60
-            return account
-        }
-    })
-    .then((account) => {
-        // Reschedule vote
-        if (recovery_wait > 0) {
-            console.log("Rescheduling ", recovery_wait, " minutes to recover")
-            var later = moment().add(recovery_wait, 'minutes').toDate()
-            schedule.scheduleJob(later, function() {
-                upvote(author, permlink, resister)
-            })
-            return account
-        }
-        return vote(author, permlink, resister, resister.upvoteWeight * 100)
-            .then((promise) => {
-                return is_already_replied_to(author, permlink)
-                    .then((found) => {
-                        if (!found) { // we we haven't replied yet
-                            return reply(author, permlink, "upvote") 
-                        }
-                        return found;
-                    });
-            });
-    })
+    vote(author, permlink, resister, resister.upvoteWeight * 100)
+    return is_already_replied_to(author, permlink)
+        .then((found) => {
+            if (!found) { // we we haven't replied yet
+                return reply(author, permlink, "upvote") 
+            }
+            return found;
+        });
 }
 
 function unvote(author, permlink, resister) {
-    return vote(author, permlink, resister, UNVOTE_WEIGHT)
+    vote(author, permlink, resister, UNVOTE_WEIGHT)
 }
 
 function vote(author, permlink, resister, weight) {
-
-    if (resister.refresh_token) {
-        return fetch_access_token(resister)
-            .then((data) => {
-                api.setAccessToken(data.access_token)
-
-                const retval = api.vote(resister.username, 
-                        author,
-                        permlink,
-                        weight,
-                        function(err, results) {
-                            if (err) {
-                                return Promise.reject(err);
-                            }
-                            return Promise.resolve(results);
-                        })
-                api.setAccessToken('')
-                return retval;                
-            })
-            .then((results) => {
-                console.log("Vote result: ", results);
-            })
-            .catch((exception) => {
-                console.log("Unable to vote ", exception)
-            })
-    }
-
-    return steem.broadcast.voteAsync(
-            resister.wif, 
-            resister.username, 
-            author,
-            permlink,
-            weight
-        )
-        .then((results) =>  {
-            console.log(results)
-        })
-        .catch((err) => {
-            console.log("Vote failed: ", err)
-        })
+    VOTING.push({ author: author, permlink: permlink, resister: resister, weight: weight })
 }
 
 function collectiveDownvote(author, permlink) {
@@ -386,16 +261,27 @@ function execute() {
             var operation_name = result[0]
             switch(operation_name) {
                 case 'comment':
-                    // processComment(result[1]);
+                    if (operation.parent_author == '') {
+                        processComment(operation)
+                            .catch((e) => {
+                                console.log("Failed to process comment ", e)
+                            });
+                    }
                     break;
                 case 'vote':
-                    // processVote(new Vote(result[1]))
+                    processVote(new Vote(result[1]))
                     break;
                 case 'unvote':
-                    // processUnvote(new Vote(result[1]))
+                    processUnvote(new Vote(result[1]))
                     break;
                 default:
             }   
         }
     })
+}
+
+function execute(voting_queue, comment_queue) {
+    VOTING = voting_queue;
+    COMMENTS = comment_queue;
+    mainLoop();
 }

Implemented functionality to push votes and comments onto a queue.

diff --git a/app/helpers/bot/vote.js b/app/helpers/bot/vote.js
new file mode 100644
index 0000000..1d6500f
--- /dev/null
+++ b/app/helpers/bot/vote.js
@@ -0,0 +1,144 @@
+const Promise = require('bluebird')
+const steem = require('steem')
+const sc2 = Promise.promisifyAll(require('sc2-sdk'))
+const { user, wif, sc2_secret, steemit_url, grumpy, blacklisted } = require('../../config')
+const schedule = require('node-schedule')
+const Sequelize = require('sequelize')
+const rp = require('request-promise');
+const moment = require('moment');
+
+steem.api.setWebSocket(steemit_url)
+
+const MINUTE = new schedule.RecurrenceRule();
+MINUTE.second = 1
+
+const SECONDS_PER_HOUR = 3600
+const PERCENT_PER_DAY = 20
+const HOURS_PER_DAY = 24
+const MAX_VOTING_POWER = 10000
+const DAYS_TO_100_PERCENT = 100 / PERCENT_PER_DAY
+const SECONDS_FOR_100_PERCENT = DAYS_TO_100_PERCENT * HOURS_PER_DAY * SECONDS_PER_HOUR
+const RECOVERY_RATE = MAX_VOTING_POWER / SECONDS_FOR_100_PERCENT
+const DEFAULT_THRESHOLD = 9000
+
+
+function fetch_access_token(resister) {
+    return rp({
+        method: "POST",
+        uri: "https://v2.steemconnect.com/api/oauth2/token",
+        body: {
+          refresh_token: resister.refreshToken,
+          client_id: "we-resist",
+          client_secret: sc2_secret,
+          scope: "vote,comment,offline"
+        },
+        json: true
+      })
+}
+
+function current_voting_power(vp_last, last_vote) {
+    console.log("Comparing %s to %s ", moment().utc().add(7, 'hours').local().toISOString(), moment(last_vote).utc().local().toISOString())
+
+    var seconds_since_vote = moment().utc().add(7, 'hours').local().diff(moment(last_vote).utc().local(), 'seconds')
+    return (RECOVERY_RATE * seconds_since_vote) + vp_last
+}
+
+function time_needed_to_recover(voting_power, threshold) {
+    return (threshold - voting_power) / RECOVERY_RATE
+}
+
+function check_can_vote(resister) {
+    return steem.api.getAccountsAsync([ resister.username ]).then((accounts) => {
+        if (accounts && accounts.length > 0) {
+            const account = accounts[0];
+            console.log("Getting voting power for %d %s", account.voting_power, account.last_vote_time)
+            var voting_power = current_voting_power(account.voting_power, account.last_vote_time)
+            if (voting_power > resister.threshold) {
+                return true;
+            }
+        }
+        return false;
+    })
+}
+
+function vote(author, permlink, resister, weight) {
+    const api = sc2.Initialize({
+        app: 'we-resist',
+        callbackURL: 'https://we-resist-bot.herokuapp.com/',
+        accessToken: '',
+        scope: ['vote', 'comment', 'offline']
+    })
+
+    if (resister.refreshToken) {
+        return fetch_access_token(resister)
+            .then((data) => {
+                api.setAccessToken(data.access_token)
+                return new Promise((resolve, reject) => {
+                    api.vote(
+                        resister.username,
+                        author,
+                        permlink,
+                        weight,
+                        function(err, results) {
+                            if (err) {
+                                return reject(err);
+                            }
+                            return resolve(results);
+                        }
+                    )
+                })
+                .then((results) => {
+                    api.setAccessToken('')
+                })
+            })
+            .catch((exception) => {
+                console.log("Unable to vote for ", resister.username, exception.message)
+                console.log("Error ", exception.error_description)
+            })
+    }
+    return steem.broadcast.voteAsync(
+        resister.wif, 
+        resister.username, 
+        author,
+        permlink,
+        weight
+    )
+    .then((results) =>  {
+        console.log("Vote results: ", results)
+        return results;
+    },
+    (err) => {
+        console.log("Vote failed for %s: %s", resister.username, err.message)
+    })
+}
+
+function execute(voting) {
+    schedule.scheduleJob(MINUTE, function() {
+        const api = sc2.Initialize({
+            app: 'we-resist',
+            callbackURL: 'https://we-resist-bot.herokuapp.com/',
+            accessToken: '',
+            scope: ['vote', 'comment', 'offline']
+        })
+
+        if (voting.length() < 1) {
+            return {};
+        }
+               
+        const { author, permlink, resister, weight } = voting.shift();
+
+        return check_can_vote(resister).then((can_vote) => {
+            if (can_vote) {
+                console.log("Voting for ", resister.username)
+                vote(author, permlink, resister, weight)
+            }
+            else {
+                voting.push({ author, permlink, resister, weight })
+            }
+        })
+    })
+}
+
+module.exports = {
+    execute
+}

Adding voting queue. Scheduling votes for every minute. Some code
was removed and placed into appropriate voting and comment
modules. Voting/commenting functionality is no longer required in here.

diff --git a/app/helpers/templates/downvote.hb b/app/helpers/templates/downvote.hb
index c4ff9b7..7bc64ab 100644
--- a/app/helpers/templates/downvote.hb
+++ b/app/helpers/templates/downvote.hb
@@ -2,6 +2,10 @@
 
 @GrumpyCat
 
+* You are flagging innocent people and calling them "collateral damage"!
+* You are trying to impose your rules by using your SP on ones weaker than you!
+* You have rejected all the diplomatic proposals we have brought so far!
+
 Your self upvote is flagged by The-Resistance team using the WE-RESIST bot.
 We will be resisting with a team, stronger each day, unless you stop downvoting innocent people.
 
@@ -9,4 +13,4 @@ https://steemitimages.com/256x512/https://steemitimages.com/DQmasLjbAiiPJKeiM6GH
 ### To your tyranny WE-RESIST
 
 The Resistance
-Discord: https://discord.gg/qMWCbWR

+Discord: https://discord.gg/qMWCbWR
diff --git a/app/helpers/templates/invite.hb b/app/helpers/templates/invite.hb
index 91db556..de62913 100644
--- a/app/helpers/templates/invite.hb
+++ b/app/helpers/templates/invite.hb
@@ -1,13 +1,17 @@
 ### WE ARE THE RESISTANCE!
 
-You are bullied by @grumpycat on your post.
-We are resisting this.
+You are bullied by @GrumpyCat on your post.
 
-Check this link,spread the word and join us if you would agree.
-https://steemit.com/the-resistance/@the-resistance/we-declare-resistance
+* @GrumpyCat is flagging innocent people and calling them "collateral damage"!
+* @GrumpyCat is trying to impose his rules by using your SP on ones weaker than him!
+* @GrumpyCat has rejected all the diplomatic proposals we have brought so far!
+
+@The-Resistance is here to stop this and protect the weaker ones from the tyranny of @GrumpyCat  
+
+Join our bot We-Resist to protect minnows  https://we-resist-bot.herokuapp.com
+Come to our house to meet with the community https://discord.gg/qMWCbWR
 
 https://steemitimages.com/256x512/https://steemitimages.com/DQmasLjbAiiPJKeiM6GHZRb9bgghMLQKP4iiUp3jCdkVJyS/image.png
 ### @GrumpyCat, to your tyranny WE-RESIST
 
 The Resistance
-Discord: https://discord.gg/qMWCbWR

Updated template literature



Posted on Utopian.io - Rewarding Open Source Contributors

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Sounds like a huge improvement to efficiency

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Hey @r351574nc3 I am @utopian-io. I have just upvoted you!

Achievements

  • You have less than 500 followers. Just gave you a gift to help you succeed!
  • Seems like you contribute quite often. AMAZING!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x