Almost everyone on hive knows what a votebot is. I'm sure quite a lot percentage of people use a votebot, wether its a service like https://hive.vote or something they programmed themselves in order to achieve certain requirements while voting. I certainly do.
Votebots aren't bad. They come in many styles. Some are out there just trying to maximize rewards by getting best possible curation. Some are discord bots where users can use to vote posts on accounts. Others may help people vote with delayed actions. And some others might just be used to help curation services like and
(you don't want to be sharing your keys out to the curators).
has been pestering me to make a votebot for LEO for quite some time. He even made
for me in order for me to do it and not mess with my normal voting(a mix of manual and automated). And yesterday I decided to actually do it. The bot isn't complicated at all. All it does is stream the chain, looking for posts made by a certain few people. If it finds a suitable post, it checks to make sure that the post is a LEOFinance post. If it meets that requirement, then it checks my LEO VP. As long as its 80% or higher it will wait three minutes and then vote the post. The accounts I chose are fully manually picked and I'll be modifying the list(along with the help of the other people who's accounts are in it) to keep it up to date with good LEO authors.
The code:
index.js
//First we need to grab all the dependencies
let hive = require("@hiveio/hive-js")
let fs = require("fs")
let axios = require("axios")
let day = require("dayjs") //Moment won't be getting any more updates so I moved to dayjs
let utc = require('dayjs/plugin/utc')
day.extend(utc)
let lastBlockParsed = -1
let accounts = [] //These are all the accounts that will be voting
let config = {} //This is the config
let targets = [] //These are all the accounts that will be voted
let gang = [] //This is the gang. Will be voted always no matter what.
hive.api.setOptions({ url: "https://api.deathwing.me/" })
startStreaming()
update()
setInterval(() => { //Once a minute (1000 ms * 60 sec) we update accounts, config, targets, gang
update()
}, 1000 * 60)
/**
* Streams
* @param {Integer} blockNumber block number to stream
*/
function getBlock(blockNumber) {
hive.api.getBlock(blockNumber, (errB, resultB) => {
if (!errB && resultB) {
if (blockNumber === lastBlockParsed + 1) {
parseBlock(resultB)
lastBlockParsed = lastBlockParsed + 1
setTimeout(() => {
getBlock(lastBlockParsed + 1)
}, 0.5 * 1000)
} else {
setTimeout(() => {
getBlock(lastBlockParsed + 1)
}, 3000)
}
} else {
setTimeout(() => {
getBlock(blockNumber)
}, 0.5 * 1000)
}
})
}
/**
* Parses a block and sends to router
* @param {Object} block Block to parse
*/
function parseBlock(block) {
if (block.transactions.length !== 0) {
let trxs = block.transactions
for (let i in trxs){
let trx = trxs[i]
parseTrx(trx)
}
}
}
/**
* Parses a trx to see if its a valid post
* @param {Object} transaction
*/
function parseTrx(trx){
let ops = trx.operations
for (let i in ops){
let op = ops[i]
if (op[0] === "comment"){
let action = op[1]
let metadata = {}
let includesLeoTag = false
try {
metadata = JSON.parse(action.json_metadata)
includesLeoTag = metadata.tags.includes("hive-167922") || metadata.tags.includes("leofinance")
} catch (e){
//Do jack shit cuz i don't error hanle
}
if (targets.includes(action.author) && action.parent_author === "" && (includesLeoTag || (action.parent_permlink === "hive-167922" || action.parent_permlink === "leofinance"))){
processLeoPost(action)
}
}
}
}
/**
* Process's post and votes
* @param {Object} post post
*/
function processLeoPost(post){
axios("https://scot-api.steem-engine.com/@rishi556.leo?hive=1").then((result) => { //Since all these accounts are only voting with this bot, checking one checks all since they should have the same amount of VP in theory. Or close enough that I don't care.
let leo_vp = parseInt(result.data.LEO.voting_power) / 100
let last_vote_time = day.utc(result.data.LEO.last_vote_time).unix()
let now = day.utc().unix()
let diff = now - last_vote_time
leo_vp = leo_vp + (0.00023148148 * diff)
if (leo_vp >= config.min_vp_vote || gang.includes(post.author)){
setTimeout(() => {
for (let i in accounts){
hive.broadcast.vote(config.posting_key, accounts[i], post.author, post.permlink, config.vote_weight, (err, result) => { // All the accounts voting have one account as posting auth and so the one key can be used on all of them
//Don't do shit either way
})
}
}, 1000 * 60 * 3.5)
}
})
}
/**
* Returns block to start streaming on.
* @returns Block to start streaming on.
*/
async function getStartStreamBlock() {
return new Promise((resolve, reject) => {
hive.api.getDynamicGlobalProperties((err, result) => {
if (err) {
console.log(err)
return reject(err)
}
return resolve(result.last_irreversible_block_num)
})
})
}
/**
* Starts streaming the hive blockchain
*/
async function startStreaming() {
let startBlock = await getStartStreamBlock()
lastBlockParsed = startBlock - 1
getBlock(startBlock)
}
/**
* Updates all 4 vars from json files
*/
function update(){
fs.readFile("config.json", (err, data) => {
config = JSON.parse(data.toString())
})
fs.readFile("accounts.json", (err, data) => {
accounts = JSON.parse(data.toString())
})
fs.readFile("targets.json", (err, data) => {
targets = JSON.parse(data.toString())
})
fs.readFile("gang.json", (err, data) => {
gang = JSON.parse(data.toString())
})
}
This is the heart of the program with all the code in it. It consists of reading json files to update some variables used. Theres also a hive streamer which checks each block for posts to make sure it fits the requirements to be voted. Could I have improved this? Yes most definitely but I wrote it in half an hour with having yelled at me for nearly 3 weeks to write this.
gang.json
[
"edicted",
"deathwing",
"cadawg",
"rishi556",
"foxon"
]
This is the gang members. People in this list get voted no matter the VP. Just an array.
config.json
{
"posting_key" : "5NOYOUWONTGETMYPRIVATEKEY123",
"min_vp_vote" : 80,
"vote_weight" : 10000
}
This lets us modify parameters for the bot to work at. Posting key is the posting key to use to vote. Min vp vote is the min vp to have in order to vote. Vote weight is what % to vote at * 100(to make it an integer since we can vote up to 2 decimal placs).
targets.json
[
"edicted"
]
These are the people we will be voting. Just another array of names.
accounts.json
[
"rishi556.leo",
"deathwing.leo",
"roar2vote"
]
These are the accounts doing the voting. As you can see, is one of them since he was pestering me to do this.
And thats all there is to it. We've had it running for nearly 24 hours so far and I see no problem. I'll probably add to it as time goes on to improve it(auto node switching on fail is my biggest thing). Enjoy the bot if you do decide to use it. LMK if you want it and maybe I'll throw it up on a git repo somewhere.