Steem-JS Following Feed Voting Bot, featuring html GUI and dynamic blacklist #3

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@loshcat·
0.000 HBD
Steem-JS Following Feed Voting Bot, featuring html GUI and dynamic blacklist #3
The first two tutorials of this voting bot:

- [Following Feed Voting Bot #1](https://utopian.io/utopian-io/@loshcat/steem-js-following-feed-voting-bot-featuring-html-gui-and-dynamic-blacklist-1)
- [Following Feed Voting Bot #2](https://utopian.io/utopian-io/@loshcat/steem-js-following-feed-voting-bot-featuring-html-gui-and-dynamic-blacklist-2)

#### What Will You Learn?

- How to obtain the posts in your feed through several Steem-JS methods and process that information to get an array of vote-eligible posts.
- How to set up minimum and maximum post maturities eligible for a vote and check for reSteems.
- How to vote on these posts while avoiding the 3 second limit and handle vote errors so failed votes do not loop forever but still make several tries.

#### Requirements

- The index.html, main.js, and main.css files covered in the first tutorials linked at the top.
- Steem-JS
- A text editor of your choice (I use Brackets).

#### Difficulty

- Basic/Intermediate

#### Tutorial Contents

The last tutorial left off on the ```getFollowingList``` function within our js which created a list of all the people you are following and that make up your feed. Because of the way I'm handling resteems however, there is a redundancy in that function. I will add the function below to both show the slight change, and to provide the jumping off point of this tutorial.

``` language javascript
function getFollowingList(nextFollow, flistArg, blacklist) {
    var account = document.getElementById('Account').value;
    steem.api.getFollowing(account, nextFollow, null, 100, function(err, result) {
        if (!err) {
            document.getElementById('Log').innerHTML = JSON.stringify(result);
            let followsList = flistArg;
            
            for (let follows of result) {
                if (/*blacklist.indexOf(follows.following) == -1 &&*/ follows.what.length > 0 && follows.what[0] == 'blog') {
                    followsList.push(follows.following);
                }
            }
            
            if (result.length == 100) {
                getFollowingList(result[result.length - 1].following, followsList, blacklist);
            } else {
                console.log(followsList);
                processStates(followsList, blacklist);
            }
        } else {
            errorHandler(err);
        }
    });
}
```

This is the function we left off on, and the ```/*blacklist.indexOf(follows.following) == -1 &&*/``` is the part which I will have solely in the next function in the chain, ```processStates```. You **do not** need to change this function as I did, and you may even want to keep it depending on how you want your following list. All that piece did was exclude blacklisted names from being in the list at all, but since we will be excluding posts later on, it was redundant for my purposes.

#### How to get your feed

Now, before I go into my next function, I'd like to go over four different ways you can get your feed in Steem-JS.

The first and the one that may seem the fastest is:
``` language javascript
steem.api.getFeedEntries('loshcat', 0, 500, function(err, result) {
  console.log(err, result);
});
```

The first argument is a string of the account whose feed you want. The second argument ```0``` is the entry id. This is an index of from where you want to start getting feed information. Using 0 simply returns the most recent as the starting point. Otherwise this could be used for something like scrolling down a page of your feed where you need to get more entries from a certain point every time you hit the bottom of the page. The last argument ```500``` is the maximum amount of entries you can get per call. The problem with using this function for our purposes though, is that the information it returns per post is:
```
author: "someone"
entry_id: 25230
permlink: "any-permlink"
reblog_by: Array []
reblog_on: "1970-01-01T00:00:00"
```

To calculate the upper and lower maturity bounds that are acceptable to vote on, we need the exact time of creation, which this function does not provide.

That said, the next Steem-JS function you could use is:
``` language javascript
steem.api.getFeed('loshcat', 0, 500, function(err, result) {
  console.log(err, result);
});
```

This call fixes our previous problem of not having the exact creation time of the post by returning an instance of the comment itself:
```
comment: Object { id: 36357876, author: "someone", permlink: "any-permlink", … }
entry_id: 25727
reblog_by: Array [ "reblogger" ]
reblog_on: "2018-03-03T05:15:39"
```
Upon opening the comment object, a large amount of information pertaining to the comment will be available including creation time. To access it all you need to do is ask for something like ```result[i].comment.created```. Something interesting though, is that this call does not return the active_votes. Without those, it will be difficult to check if you've already voted on a post to avoid revoting errors.

So, we continue the search for a way to get all the information we need with a single call and find the following function:
``` language javascript
let query = {
  tag: 'loshcat',
  limit: 100
};

steem.api.getDiscussionsByFeed(query, function (err, result) {
  console.log(err, result);
});
```

Finally we have found a Steem-JS call that returns the full information we need to make a calculated vote. The resulting array is a list of the 100 most recent comments made to someone's feed. While it does have active_votes, created, author, and permlink, there is one more Steem-JS function I know of we can use. I have yet to see it used this way, and was surprised to find it worked, but ```getState``` is also an option:
``` language javascript
steem.api.getState('/@loshcat/feed', function(err, result) {
  console.log(err, result);
});
```

This function returns an object containing more than we need, but to demonstrate its use since it's rare for things like this, I will use it for my bot. One nice thing about using this function is the key:value naming in the content object. The key names for the feed posts are in the format: author/permlink. And the value is the post information itself.

Now with all of these options to get our feed, or anyone's feed really, I will finally continue on to the next function in our chain.
#### main.js
``` language javascript
function processStates(followsList, blacklist) {
	var accountFeed = String('/@' + document.getElementById('Account').value + '/feed');
	steem.api.getState(accountFeed, function(err, result) {
		if (!err) {
			voteQualifiers = [];
			let activeVotes = [],
				followsCrossRef = String(''),
				resteemCheck = false,
				postsDate,
				lowLimDate,
				upLimDate,
				nowDate;
			
			for (let post in result.content) {
				activeVotes = result.content[post].active_votes,
					activeVotes = activeVotes.map(function(voteObj) {
						return voteObj.voter;
					}),
					followsCrossRef = result.content[post].author,
					resteemCheck = false,
					postsDate = new Date(result.content[post].created + 'Z'),
					postsDate = postsDate.getTime(),
					lowLimDate = postsDate + (document.getElementById('LowLim').value * 60000), // minutes * amount of milliseconds in a minute
					upLimDate = postsDate + (document.getElementById('UpLim').value * 60000),
					nowDate = new Date,
					nowDate = Date.UTC(nowDate.getUTCFullYear(), nowDate.getUTCMonth(), nowDate.getUTCDate(), nowDate.getUTCHours(), nowDate.getUTCMinutes(), nowDate.getUTCSeconds(), nowDate.getUTCMilliseconds());
				
				if (document.getElementById('UpResteems').checked == false) {
					for (var i = 0; i < followsList.length; i++) {
						if (followsList[i] == followsCrossRef) {
							resteemCheck = true;
						}
					}
				} else {
					resteemCheck = true;
				}
				
				if (blacklist.indexOf(followsCrossRef) == -1 && activeVotes.indexOf('guest123') == -1 && resteemCheck == true && nowDate >= lowLimDate && nowDate <= upLimDate) {
					voteQualifiers.push(post);
				}
			}
			document.getElementById('Log').innerHTML = voteQualifiers.length == 0 ? 'No posts to vote right now.' : voteQualifiers;
			
			voteIteration = Number(0);
			console.log(voteQualifiers);
			votePosts(0);
			
		} else {
			errorHandler(err);
		}
	});
}
```

The first variable is formatting a path for the Steem-JS function by using the html input element so the feed fits the following list and can be anyone, meaning you can vote on whoever's feed you want. Since I will hard code guest123 to be the account checked for voting power and for voting for this tutorial, you can send it to different feeds for easy testing before replacing it with yourself or creating more input elements.

After I call the function using that path, I reset the voteQualifiers which is the post list we vote on. Then I set up the variables needed for each post in the feed. ```for (let post in result.content)``` uses ```post``` as a placeholder reference for each comment object ```in``` the result.content key. This lets us iterate over objects whose names are dynamic but it is not in order and so not for arrays hence we use ```of``` for those as seen in the previous function. The first interesting part of the loop is:
``` language javascript
activeVotes = result.content[post].active_votes,
					activeVotes = activeVotes.map(function(voteObj) {
						return voteObj.voter;
					});
```

To use a key you do not know the name of, for example: result.content.dynamicName where the dynamicName could be anything, you can use a variable reference from a loop and put brackets around it instead of a dot. This way, we get an array of the vote objects representing each persons vote on that post. To make this an array of only the names of the voters, then we do ```.map``` with the arbitrary reference to each object in the array and return the voter to recreate a more concise array. The variable assigning and date math after is similar to what we did in the vpFormater function so on to the resteem checker.

The resteem check could be done in many ways but I will go with a more 'analog' way of doing it to illustrate the simplicity. If the box is not checked, then we want to avoid voting on posts not made specifically by who we follow. To do this, I just iterate over each name in our followsList passed in from the previous function and see if it's the same as the author of the post we're currently on. If we get a hit, then we flip a boolean variable to true so it passes this test in our main ```if``` gatekeeper. If the name is never found, the value stays false and forces the post to **not** be included. In the case that we do want to upvote resteems, Then this specific test defaults to true for each post.

After that,
``` language javascript
if (blacklist.indexOf(followsCrossRef) == -1 && activeVotes.indexOf('guest123') == -1 && resteemCheck == true && nowDate >= lowLimDate && nowDate <= upLimDate) {
					voteQualifiers.push(post);
}
```

takes all of the data gathered above and simply checks
- if the author is on the blacklist.
- if we have already voted on it.
- if it passed the resteem check.
- and if the time since it was posted is between our eligibility thresholds.

Passing each requirement gets the post qualified and thus pushed into the voteQualifiers array. Then I just use a ternary for what to log depending on if anything qualified, and reset my voteIteration variable in preparation for the final voting function. Here it is:

#### main.js
``` language javascript
function votePosts(errBreak) {
	if (voteIteration != voteQualifiers.length && voteQualifiers.length != 0) {
		let splitterRef = voteQualifiers[voteIteration].split('/');
		steem.broadcast.vote('5JRaypasxMx1L97ZUX7YuC5Psb5EAbF821kkAGtBj7xCJFQcbLg', 'guest123', splitterRef[0], splitterRef[1], 1000, function(err, result) {
			if (!err) {
				voteIteration++;
				totalVotes++;
				document.getElementById('TotalVotes').value = totalVotes;
				document.getElementById('Log').innerHTML = JSON.stringify(result);
				setTimeout(votePosts, 3200, 0);
			} else {
				errorHandler(err);
				errBreak++;
				if (errBreak >= 2) {
					voteIteration++;
					errBreak = 0;
				}
				setTimeout(votePosts, 3200, errBreak);
			}
		});
	}
}
```

First I make sure that posts did qualify and that the voteIteration has not gone through each already. Then I use the fact that the names of the posts included the author and permlink separated by a "/" and create an array with the contents being anything split at the "/". Then I plug the array values and set a voting weight of 10%. You can set up an html input for that as well. If the vote goes through, I iterate my global ```voteIteration``` variable and use ```setTimeout``` to chain the voting function again after waiting a safe 3.2 seconds to avoid the 3 second limit.

Where it gets interesting is the argument passed into the voting function. If you noticed, we started by passing in ```0``` from the previous function and call the argument errBreak. This is because in the case of an error, I want to try a few times before I give up on that specific post. In the ```setTimeout``` and ```setInterval``` functions, the third argument and so on gets passed into the actual function. As a bonus it also creates an instance of whatever you passed in so the variable could change but the interval would keep the original reference at least for the timeout. Nonetheless, the error handling in the vote function forces however many tries you want for a failed vote before moving on to the next with ```if (errBreak >= 2)```, the ```2``` being the amount of tries. It also illustrates how I try to keep my scopes contained during delayed loops needed for situations like voting.

Hope you enjoyed the tutorials and learned something new. This concludes the base functionality of a Steem-JS Following Feed Voting Bot which has a customizable html GUI, dynamic blacklist and any lists/inputs from html, ways to obtain posts in your feed, and utilities/tidbits along the way to make things easier to understand and improve functionality. With everything combined from these three tutorials, the previous two linked at the top, you should be able to double click on the index.html, set up the control panel, and watch the voting in action. If anyone is interested, I also have a tutorial on Steemit for how to have this run easily on a android phone. That tutorial is [here.](https://steemit.com/utopian-io/@loshcat/how-to-set-up-your-own-home-brew-server-on-a-mobile-phone) I have been testing it for a while now, and it has run uninterrupted for several weeks despite network errors and my internet giving out at one point. It has been a fun project and I hope to expand on it further. As always, have a good one!

<br /><hr/><em>Posted on <a href="https://utopian.io/utopian-io/@loshcat/steem-js-following-feed-voting-bot-featuring-html-gui-and-dynamic-blacklist-3">Utopian.io -  Rewarding Open Source Contributors</a></em><hr/>
👍 , , , , , , , , ,