TUTORIAL - Reblog Contest - Building With Steem-js #10

in utopian-io •  7 years ago  (edited)

bot-tut-temp.png

This tutorial is part of a series on ‘How To Build Bot’s Using Steem-JS Open Source Library’ it aims to demonstrate practical applications for bots and demystify the code used to create them.

The completed bot code is here - I know some people find it easier to see everything in one block and follow along that way. ⬅️

Outline 📝

In this tutorial we’re going to create another bot to assist with running tutorials. The bot will not respond directly but will help gather a list of accounts who have re-steemed a specific post. For examples - used for contests that ask entrants to Re-steem for a chance to win. This was inspired by @money-dreamer who posted about wanting to do exactly this.

Requirements

  • A plain text editor (I use atom.io )

Difficulty

intermediate

Please run through all other tutorials in the series to better understand what we’re working on.

(I appreciate feedback on the teaching level).

Learning Goals

  • re-use knowledge from previous tutorials
  • Look at the getBlock() function of Steem-js
  • create a that finds re-blogs of a certain Steem post

Step 1 - information 💻

A couple points to note before starting. When accessing data about particular posts on the blockchain there is a reblogged_by property. Unfortunately, the reblogged_by always returns empty, regardless of the number of reblogs(confirmed through the web interface). There is no information in the Steem-js library about re-blogs and I have found almost no information online.

reblogs are broadcast across the network as custom_json and can be recorded they are just not part of the steem-js database API. We have two options.

1). Stream transactions and save any custom_json transactions that match our required data. This is the plan today.

2). Use a third-party set of Data that has already been databased and streams the transactions themselves. see this post for an example of this method, requires a paid subscription to third-party service.

Note that today's tutorial might not be the fastest or most technically sound method of retrieving this data but in order to keep the tutorial available for a wider advice I’ve decided to keep it as simple as possible.

Step 2 - Setup 📈

We’re once again working in the browser, but just as easily could work from node.js. Starting template with script tags setup

To kick things off we’re going to want four pieces of information that relate to the post we’re checking for reblogs. First is the permlink and can be seen in the URL of the post.

Screen Shot 2018-02-11 at 16.04.20.png

const TARGET_PERMLINK = 'finally-a-comments-system-that-rewards-its-authors-steemcomments-js-got-an-upgrade'

Next is the block number where our post was included into the blockchain. (More on why in the next section). The easiest way I’ve found to find this is on steemd for example I would navigate to my page https://steemd.com/@sambillingh and my recent activity will show in the feed. In the top right-hand corner of each activity there is a transaction id link, click that to find specific information about the transaction. Here we can see the exact block number the post was included in.

Unfortunately I’ve not found a way to retrieve this from the steem-js API

Screen Shot 2018-02-11 at 16.08.55.png

Screen Shot 2018-02-11 at 16.09.12.png

Store that block number as a variable.

const BLOCK = 19642524

Third, also available on the transaction screen in the time of the block we’re interested in. To make out date times easier to work with we’ll create a javascript data object and use valueOf() this gets the milliseconds between 1 January 1970 00:00:00 UTC (know as The Unix Epoch) and our date. Having a single integer number that represents the date make it really easy to check if one time is before or after another by using greater than or less than.

``const BLOCK_TIME = new Date('2018-02-06T20:59:36').valueOf()```

Finally we’ll create an end time. Our script will stop after this time. My full set of starting information.

const TARGET_PERMLINK =  'finally-a-comments-system-that-rewards-its-authors-steemcomments-js-got-an-upgrade'
const BLOCK = 19642524
const BLOCK_TIME = new Date('2018-02-06T20:59:36').valueOf()
const END_TIME = new Date('2018-02-07T20:59:36').valueOf()

Step 3 - Loop Over Steem Blocks 🏋️‍♂️

Block on the Steem Blockchain are created roughly every 3 seconds by one of the Steem witnesses and contain a number of transactions. I’ve observed roughly 50 transactions per block, but I’ve not looked into this too much and likely fluctuates because of a number of variables.

We have a couple options. A) We could start from the latest Steem block and work backwards through each block until we get to the time the competition started. B) We can start from the point the post was created then work forwards until a certain time/date perhaps 24hours or 7 days etc.

We’re going to go for option B today and that’s why we looked for the block number in step 2.

The getBlock() function of the Steem-js API allows us to request individual block information. We can investigate the transactions included.

steem.api.getBlock(BLOCK, function(err, block) {
    console.log(err, block);
});

Each block contains an array of transactions.

Screen Shot 2018-02-11 at 17.10.03.png

The plan is to check each block individually. As we know the current block number if we want to move onto the next one when done all we need to do is increase the block number by + 1.

first, we’ll turn the API call into a reusable function. To ensure our we don’t stream block forever we’ll add an if statement to check if the current blockTime is past our initial data of END_TIME

function getBlock(blockNumber, blockTime){
    if (blockTime < END_TIME){
        steem.api.getBlock(blockNumber, function(err, block) {
            console.log(err, block);
        });
    }
}

Once we’ve checked this first block we should immediately move to the next block on the chain. We’ll also store the timestamp to check against our if statement.

let nextBlock = blockNumber + 1
let blockTime = new Date(block.timestamp).valueOf()

This will sit inside of our getBlock function along with another call to getBlock creating a recursive function that stops only when our if statement does not pass.

function getBlock(blockNumber, blockTime){
    if (blockTime < END_TIME){
        steem.api.getBlock(blockNumber, function(err, block) {
            let nextBlock = blockNumber + 1
            let timestamp = new Date(block.timestamp).valueOf()
            getBlock(nextBlock, timestamp)
        });
    }
}

This function will now run continuously through all of the blocks until we reach our end time. FYI that’s a lot of blocks. Estimate one block every 3 seances gives us 28,800 blocks over a 24hour period we need to investigate. 🙈

Without worrying too much about how long this will take we’ll look at finding the reblog data

Step 4 - Check Transactions

Similar to what we’ve done in previous tutorials we’ll loop over transactions first checking for custom_json then checking if the type of custom_json is reblog before finally accessing the data. Passing a transactions array in preparation for use within the getBlock function.

function checkTransactions(transactions, timestamp){
      transactions.forEach(function(tx) {
                console.log(tx)
            })
}

Access the transactions array within the getBlock() response with block.transactions.

Time to narrow down the results. We’ve done this before in previous tutorials so to keep things short we’ll inspect the operations array of each transaction storing the type and data. Following that if we see a type of custom_json we’ll check the JSON property to see if it includes reblog.

transactions.forEach(function(tx) {
    let txType = tx.operations[0][0]
    let txData = tx.operations[0][1]
        if(txType === 'custom_json') {
                        let jsonData = JSON.parse(txData.json)
                    if( jsonData[0] == 'reblog' ){
                                console.log(tx)
                        }
            }
})

We’re closer. Now we’ll compare the permlink in the transaction with the one of the post we’re look at for the competition. To tie everything together we can do two things, first push the data into an array for later use and second display it on the screen.

if( jsonData[0] === 'reblog' && jsonData[1].permlink === TARGET_PERMLINK) {
              console.log(jsonData[1])
              reblogs.push(jsonData[1])
              $('body').append(`<p><a href="https://steemit.com/@${jsonData[1].account}/">${jsonData[1].account}}</a>" : ${timestamp}</p>`)
}

We’re simply appending this data to the screen here. Check the previous tutorial if you’re interested in creating a table and choosing a random entry.

p.s make sure you’re adding your checkTransactions() function to the getBlock() function.

It took 3 minutes and 580 requests to the Steem API to find my first reblog. Thank for the reblog @jeffbernst.
Screen Shot 2018-02-11 at 22.01.25.png

Screen Shot 2018-02-11 at 22.01.46.png

step 5 - Thoughts and Optimisations 🚀

It’s worth noting that this process takes time. There are a lot of blocks to work through. When looking at the network tab my requests take ~90ms. At 28,000 blocks added to the blockchain each day, it will take ~45minutes to request one day worth of transactions.

Is this the best way to get this data? Perhaps not but I think it’s important to illustrate the technique. While it’s certainly possible to use this technique it would be better to create a database that holds all of the Steem data as seen with [SteemData] & [SteemSQL]. Keep that data up to date and then make juries against it.

The script we’ve crated works through block by block in a sequence, this helps to keep the tutorial accessible. If you’re looking to use this code more than as a proof of concept I would certainly think about how you might asynchronous make the queries to the blockchain and run requests in parallel to reduce wait time.

Here’s the full code 🤖

Let me know what you think of this robot tutorial.

Other Posts in This series



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:  

Thank you for the contribution. It has been approved.

Cool tutorial once again! I can't believe steem-js doesn't have a function similar to the get_reblogged_by() function in steem-python! Maybe you can call the Steemit API directly instead of iterating over all the blocks (not sure how this works and I could be completely wrong)?

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

Thanks Amos.

I didn't think to check what steem-python does. Interesting to hear it has a built in API call, that must mean there is a correlating endpoint. I'm going to take a closer look tonight and see if I can make the call directly or add a method to steem-js. I need to learn more about the steemd nodes and how the libraries interact myself. It's an intresting learning exerise to iterate the blocks but pretty unrealistic to do it every time you want the data.

  ·  7 years ago (edited)

The actual steem-js api has got the getRebloggedBy() . I corrected this on my comment to @money-dreamer after he gave us that nice little brain teaser, which maybe should not have been so much of a tease if I just RTFM but they make it difficult to find the right manual since this is hosted by steemconnect

https://v2.steemconnect.com/docs/steemjs
https://v2.steemconnect.com/docs/steemjs#api/get_reblogged_by

can include using the cdn link
https://cdn.steemjs.com/lib/latest/steem.min.js
and it will work with the nice little default code as in examples

function reblogged(){
    steem.api.getRebloggedBy("sambillingham","tutorial-reblog-contest-building-with-steem-js-10", function(err, result) {
        console.log(err, result);
      });
}
reblogged();

Nice work! You're right it's a little difficult to know where to look for the correct information. I had not seen the Steemconnect hosted docs before. I was sure I searched the Github repo for 'reblogged' but perhaps not as I just went back and easily found it in the methods.js file although it easy to get thrown off as the method names are not in camel case.

Thanks for posting this, as interesting as the tutorial is getRebloggedBy() is the way to go!

Hey dude!

Please let me know if I'm being annoying.... but as well as the 24/7 hosting service tutorial, I'd also really like to build a bot that accepts user commands. I know Steembay uses bid bot and there are a couple of others around. I've got at least 3 ideas for this but no idea how to get the not to read the command. Bid bot uses the command Bid 10 to allow a user to bid 10 SBD.

haha no don't worry it's not annoying at all, I'm just glad you and other people find these useful. Doing a server setup next!
For the commands do you mean sending a command via memo?

Thanks man! I'm fully pumped for it.

No, I actually meant a command in the comments of a post... so all the steembay posts can use a bot by stating "bid 10" in the comments. The bot then conducts an auction with that user bidding 10 SBD. One of the things I'd like to build is a random generator so a user could comment @bot 1:45 which would generate a random number between 1 and 45. Does that seem doable?

Great tutorial!

EDIT: I subconsciously copied this comment from above.

Thanks @sambillingham, I'm also a developer and looking for a way to create my own steem-js application/bots but luckily I found these series, It's been quite helpful.

I hope that you'll create more series regarding open-source steem-js library or any series regarding development.

Thanks for an amazing series. Keep it up!

Hey, great tutorial!

I'd never thought about adding a bot for that. I may need to offer a bounty to have someone build one for me, as I know that me doing it and rebugging it, I'll be old before it's finished. I'm a writer, not a coder. LOL

I'll be sure and mention it in an upcoming article. Also, if you're interested, you can share your best tip and win some SBD. Just go here. Follow @dolphinschool for lots of great steemit training! @markrmorrisjr

@sambillingham, Upvote is the only thing I can support you.

Appreciate you stopping by, thanks.

thats really useful for me thank you so much man !

Very in depth and informative, can't imagine the time it took bashing your head against a keyboard to figure this out. Great job.

Zrzeszenie Polaków na steemit. Zapraszamy! :p im więcej nas będzie tym lepiej będziemy mogli prowadzić nasze konta na steemit...
Like i follow @polandsteemit

Hey @sambillingham 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!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

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

Oh no best post by @sambillingham . Thanks to your post