[Tutorial] Who replied to you the most? (using SteemJS)
I'm building an app called Spectacles, which shows who replies the most to your posts and comments (among many other things!).
I already explained in an earlier post how to count who upvoted you the most, so counting your replies seemed like a fun next step. This time I'm making it a bit more interesting though: we're going to grab the replies on both your regular posts and your comments, to get a more complete picture of your most loyal followers.
It also adds another challenge: the information about upvotes is embedded in each post, but the replies aren't! After loading your posts we'll need to load all the comments for each of them. Let's see how we can tackle that in an elegant way.
Some basics
I'm using a lot of ES6 features which may not always be very clear to beginning JS programmers. I do this because I really like the clean, functional code you can write with it. I always try to name my functions and variables as clear as possible, so if you don't understand a particular piece of code hopefully the name of it can give you a hint.
For this post you might want to refer to the documentation on reduce() and destructuring as well as the Map object if some code seems complicated. Or ask me anything in the comments of course!
Outline of the code
Let's start with a rough outline of the code we need. Here's what we need to do:
- Fetch a user's latest comments and posts
- Get all the replies to each of those
- Count which author replied the most (and preferrably sort by who replied the most)
- Output the result
The first two bullet points require calls to the SteemJS API, so they will return promises. We can use the result of those promises as the input for the next function. Let's use the following outline for that:
getPostsAndComments('pilcrow')
.then(getAllRepliesPerPost)
.then(allRepliesPerPost => {
// do magic here and output it
});
From this we can gather that getPostsAndComments()
should return a list of all your posts and comments, which is passed as an argument to getAllRepliesPerPost
(
note: a comment really is just a type of post. I'll be referring to both just as "post" from here on out. Adding "and comments" everywhere seems a bit redundant.
The getAllRepliesPerPost
in turn should return a list of all the replies for each of those posts, which is used as an argument allRepliesPerPost
in the last function, where we will count it all up.
getPostsAndComments(user)
There are two different API calls we need to do here, and each expects a different set of arguments (don't ask me why, the Steem API isn't always very consistent). I already covered the API call for posts in my tutorial about counting upvotes.
The method to get your comments is getDiscussionsByComments()
in which we pass a query object with the start_author
(username) and the amount of comments we want. More details about the query object and different getDiscussionsBy*()
methods can be found in @jfollas' tutorial about Retrieving Content with getDiscussionsBy*().
We want to wait until both calls are done before adding them together, which we can do with Promise.all()
. It expects an array of promises, and when all are finished it passes along the results of each call in the order you supplied them.
Adding it all together:
function getPostsAndComments(username) {
return Promise.all([
steem.api.getDiscussionsByAuthorBeforeDate(username, '', '2100-01-01T00:00:00', 10),
steem.api.getDiscussionsByComments({start_author: username, limit: 10})
]).then(([posts, comments]) => posts.concat(comments));
}
I'm limiting both calls to 10 to make testing it a little faster, but you could use a higher number up to 100. To get more than 100 requires more effort which I'll cover in a later tutorial.
getAllRepliesPerPost(posts)
Now that we have all the posts and comments, we can start fetching all the replies. Again @jfollas already covered this subject in Retrieving Comments with getContentReplies, so I'll skip over the details.
What's more important is that we have an array of 20 posts (10 from each call in the previous function), for which we want to get the replies of each. That means we have to do 20 API calls to get all the information we need. This is where the architecture of the Steem API really starts to show it's flaws, but I'll leave that rant for later.
To make 20 API calls and wait for the result we can use Promise.all()
again, but first we have to turn the array of posts into an array of promises.
function getAllRepliesPerPost(posts) {
const postRepliesPromises = posts.map(getReplies);
return Promise.all(postRepliesPromises)
}
function getReplies(post) {
return steem.api.getContentReplies(post.author, post.permlink);
}
Counting all the replies
Now that we have all the replies, we can start counting them. The result from the previous function is a nested array (each post in the original array was replaced with a new array of replies). First we flatten that into one long array, so that we can loop over it more easily later.
const toFlatArray = (all, current) => all.concat(current);
const allReplies = allRepliesPerPost.reduce(toFlatArray);
Then we use another reducer to count the occurances of each reply author, which we'll do in a Map
. For every reply in allReplies
we check if the author already has a count, otherwise we use 0. Then we add 1 to that number and put it back into the Map.
function countAuthors(allReplies) {
return allReplies.reduce((counter, reply) => {
const count = counter.get(reply.author) || 0;
return counter.set(reply.author, count + 1);
}, new Map());
}
And now that we have our count we can turn that back in to an array of key-value pairs and sort it by value (highest first):
const sortMapByValueDesc = (a, b) => b[1] - a[1];
const sortedByCount = [...countReplies.entries()].sort(sortMapByValueDesc);
The complete function:
getPostsAndComments('pilcrow')
.then(getAllRepliesPerPost)
.then(allRepliesPerPost => {
const allReplies = allRepliesPerPost.reduce(toFlatArray);
const countReplies = countAuthors(allReplies);
const sortedByCount = [...countReplies.entries()].sort(sortMapByValueDesc);
console.log(`You have ${allReplies.length} replies by ${countReplies.size} users!`);
console.log('Users who upvoted you the most: ', sortedByCount);
});
Demo on jsFiddle
No tutorial is complete without a demo: https://jsfiddle.net/52k35t9n/20/
Play around with it, enter some higher limits and see who your most loyal follower is!
If you want to know more about your most loyal followers (like the combined statistics of their replies and upvotes to you, or how much money their upvotes already earned you) I suggest you follow @spectacles. We're launching the first version very soon!
Excellent @pilcrow!
I'm glad that you included a jsFiddle demo. I played around with it a little. This is exactly the kind of thing that I'd like to learn how to do. Keep up the great work!
Thanks! I didn't have the demos in my earlier tutorials, but I figured this way people didn't need to do any complicated things to get it running :). Glad you liked it!
Very nice initiative. Will follow.
Thanks, we're trying to get the app running as soon as possible. Stay tuned :)
I will, for sure. I already followed :) Its great to have people like you who becomes a change to change things.
Yes such a good idea. But is it already working ? On the exemple it seems miss somes.. Maybe I mistake.
Anyway thanks for the initiative and the work it gives. I'll have a look to the one for the upvotes. Have a good evening !
.................
@permatek - My last post : How Silicon Valley discovered LSD
I think it should work. I'll check for you tomorrow, do you miss anything in particular? Thanks for trying it out!
I'll try again tomorrow and see on my comments but I thought that some people commented more than twice.. Not only steemboard ;) But maybe I mistake thinking too at the conversations on others posts' comments
It's only fetching data from the latest 10 posts and comments now. If you turn those numbers up (in getAllPostsAndComments) you'll get better results.
Ow okay. Thank you I'll try like this. Thanks again
Congratulations! This post has been upvoted from the communal account, @minnowsupport, by Phtephan from the Minnow Support Project. It's a witness project run by aggroed, ausbitbank, teamsteem, theprophet0, someguy123, neoxian, followbtcnews/crimsonclad, and netuoso. The goal is to help Steemit grow by supporting Minnows and creating a social network. Please find us in the Peace, Abundance, and Liberty Network (PALnet) Discord Channel. It's a completely public and open space to all members of the Steemit community who voluntarily choose to be there.