placeholderfeatureplaceholdersliderplaceholderthumb
Using get-single-post to Get Comments

Using get-single-post to Get Comments

by kitty4d

so i pushed a lot of changes to my website earlier tonight, i’d been working on things all afternoon and evening. one of the big things i did was to go ahead and start retrieving comments for posts that i display. why don’t i just post my entire file? LOL

i’ll update this page later though.

const fs = require('fs');

// const fetch = require('node-fetch');
const Cache = require("@11ty/eleventy-cache-assets");
const default_apiurl = 'https://api.love4src.com/api/v0/';

const blacklistedUsers = [
'BC1YLhvijFzT6oWzdEMq9UCmbPiXyWA2JNPbmdfE8vR4t86nsbG2TrB',
'BC1YLh8R6rVe2NVYKgWwGs9j4w3PWy1qL5wWqL96UWYwmBWLaqz6Pa1',
'BC1YLfiuB9oWiHDxntaWJWKkYCCQ7Uo2qWfLuJY4L5tP2rX7dunkZwS',
'BC1YLiESU8MTDmNgyG3vyDGyJF8yShiPEuet5ZngCEQMFg7hZsnaDBr',
'BC1YLh3aFdKKtjNRkAKku1YQPDp5sHzEqTc8Uje2RhtfNVQqQRxLDYq',
'BC1YLis25e9YzDHeKEaHu9ud7hXaQh7GiBok2hiLEWd7TRQGPu9ge7i',
'BC1YLhRXKKsRHbKdpAPDG61gHUhRAvLbZJhz3ja2nfcMAZmS5byg2uK',
'BC1YLh15YUgb5deNU4e9h9jyHefUdo7E4SAVxLAwpYWevugavLCJoEs',
'BC1YLhZxgr1dYD6SQHzWfyjkkCb5nMNpZpvAtjnHcsstoQYkA575m2i',
];

async function fetchCache(url, params) {
/* pseudoGetParams will be appended to the URL in order to maintain a cache per unique API call
otherwise, a call to get the number of followers for a user and a call to get the number of followings for a user will
be cached as a shared object, which is not the expected behavior.
i had expected the caching to take into account POST parameters the same way it does GET parameters, but
since it doesn't, this seems to be a working solution.
*/

const pseudoGetParams = params.body ? encodeURIComponent(params.body) : '';
let res = await Cache(url + '?' + pseudoGetParams, {
duration: "1d",
type: "json",
fetchOptions: params,
});
//console.log(res);
return await res;
}

class DesoLite {

constructor({ apiurl = default_apiurl, pkey = '', userName = '' }) {
this.apiurl = apiurl;
this.pkey = pkey;
this.userName = userName;
}

/*---- general ----*/

async getAppState() {
const url = this.apiurl + 'get-app-state';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PublicKeyBase58Check: this.pkey,
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

/*---- notifications ----*/

async getNotificationsByPublicKey({ pk = this.pkey, startIndex = null, numToFetch = 200 }) {
const url = this.apiurl + 'get-notifications';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PublicKeyBase58Check: pk,
FetchStartIndex: startIndex,
NumToFetch: numToFetch
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

/*---- user methods ----*/

async getUsers({ lstPk = this.pkey, skipHodlings = true }) {
const url = this.apiurl + 'get-users-stateless';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PublicKeysBase58Check: [lstPk],
SkipHodlings: skipHodlings
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

async getUserProfileByPublicKey({ pk = this.pkey, userName = this.userName }) {
const url = this.apiurl + 'get-single-profile';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PublicKeyBase58Check: pk,
Username: userName
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

/*
orderBy : "influencer_stake", "influencer_post_stake", "newest_last_post", "newest_last_comment", "influencer_coin_price"
*/

async getUserProfiles({ pk = this.pkey, userName = this.userName, usernamePrefix = '', fetchUsersThatHodl = false, numToFetch = 1, orderBy = 'newest_last_comment' }) {
const url = this.apiurl + 'get-profiles';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
UsernamePrefix: usernamePrefix,
OrderBy: orderBy,
ReaderPublicKeyBase58Check: pk,
FetchUsersThatHODL: fetchUsersThatHodl,
PublicKeyBase58Check: "",
Username: userName,
Description: "",
NumToFetch: numToFetch,
ModerationType: "",
AddGlobalFeedBool: false
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

/*---- posts methods ----*/

/*
orderBy : "newest", "oldest", "last_comment"
*/

async getPosts({ pk = this.pkey, postHashHex = null, orderBy = '', postContent = '', numToFetch = null, fetchSubComments = false, getPostsForFollowFeed = false, getPostsForGlobalWhitelist = false, getPostsByDESO = false, mediaRequired = false, postsByDESOMinutesLookback = null, addGlobalFeedBool = false }) {
const url = this.apiurl + 'get-posts-stateless';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
ReaderPublicKeyBase58Check: pk,
PostHashHex: postHashHex,
OrderBy: orderBy,
StartTstampSecs: null,
PostContent: postContent,
NumToFetch: numToFetch,
FetchSubcomments: fetchSubComments,
GetPostsForFollowFeed: getPostsForFollowFeed,
GetPostsForGlobalWhitelist: getPostsForGlobalWhitelist,
GetPostsByDESO: getPostsByDESO,
MediaRequired: mediaRequired,
PostsByDESOMinutesLookback: postsByDESOMinutesLookback,
AddGlobalFeedBool: addGlobalFeedBool
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

async getPostByPostHashHex({ pk = this.pkey, postHashHex = null, fetchParents = null, commentOffset = null, commentLimit = null, addGlobalFeedBool = false }) {
const url = this.apiurl + 'get-single-post';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
ReaderPublicKeyBase58Check: pk,
PostHashHex: postHashHex,
FetchParents: fetchParents,
CommentOffset: commentOffset,
CommentLimit: commentLimit,
AddGlobalFeedBool: addGlobalFeedBool
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

async getPostsForPublicKey({ pk = this.pkey, userName = this.userName, readerPk = null, numToFetch = null, mediaRequired = false, lastPostHashHex = null }) {
const url = this.apiurl + 'get-posts-for-public-key';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PublicKeyBase58Check: pk,
ReaderPublicKeyBase58Check: readerPk,
Username: userName,
MediaRequired: mediaRequired,
LastPostHashHex: lastPostHashHex,
NumToFetch: numToFetch,
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

async getLikesByPostHashHex({ pk = this.pkey, postHashHex = null, offset = null, limit = null }) {
const url = this.apiurl + 'get-likes-for-post';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PostHashHex: postHashHex,
ReaderPublicKeyBase58Check: pk,
Limit: limit,
Offset: offset
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

async getRecloutsByPostHashHex({ pk = this.pkey, postHashHex = null, offset = null, limit = null }) {
const url = this.apiurl + 'get-reposts-for-post';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PostHashHex: postHashHex,
ReaderPublicKeyBase58Check: pk,
Limit: limit,
Offset: offset
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

async getQuotesByPostHashHex({ pk = this.pkey, postHashHex = null, offset = null, limit = null }) {
const url = this.apiurl + 'get-quote-reposts-for-post';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PostHashHex: postHashHex,
ReaderPublicKeyBase58Check: pk,
Limit: limit,
Offset: offset
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

async getDiamondsByPostHashHex({ pk = this.pkey, postHashHex = null, offset = null, limit = null }) {
const url = this.apiurl + 'get-diamonds-for-post';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PostHashHex: postHashHex,
ReaderPublicKeyBase58Check: pk,
Limit: limit,
Offset: offset
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

/* association methods */

async getHodlersByPublicKey({ pk = this.pkey, userName = this.userName, fetchAll = true, fetchHodlings = false, numToFetch = null, lastPk = null }) {
const url = this.apiurl + 'get-hodlers-for-public-key';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PublicKeyBase58Check: pk,
Username: userName,
LastPublicKeyBase58Check: lastPk,
NumToFetch: numToFetch,
FetchHodlings: fetchHodlings,
FetchAll: fetchAll
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

/*
get users who have given diamonds, how many, and highest diamond level -
https://github.com/deso-protocol/frontend/blob/e006beb72867f6d48a78adb1d126c66144a4298c/src/app/creator-profile-page/creator-diamonds/creator-diamonds.component.ts#L41
*/

async getDiamondsByPublicKey({ pk = this.pkey, fetchYouDiamonded = false }) {
const url = this.apiurl + 'get-diamonds-for-public-key';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PublicKeyBase58Check: pk,
FetchYouDiamonded: fetchYouDiamonded
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}

/* examples:
get total # of followers/following for a user -
https://github.com/deso-protocol/frontend/blob/e006beb72867f6d48a78adb1d126c66144a4298c/src/app/creator-profile-page/creator-profile-top-card/creator-profile-top-card.component.ts#L161
get the profiles of those users -
https://github.com/deso-protocol/frontend/blob/e006beb72867f6d48a78adb1d126c66144a4298c/src/app/manage-follows-page/manage-follows/manage-follows.component.ts#L54
*/

async getFollowsByPublicKey({ pk = this.pkey, userName = this.userName, getFollowersOf = false, numToFetch = null, lastPk = null }) {
const url = this.apiurl + 'get-follows-stateless';

try {
let res = await fetchCache(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
PublicKeyBase58Check: pk,
Username: userName,
GetEntriesFollowingUsername: getFollowersOf,
NumToFetch: numToFetch,
LastPublicKeyBase58Check: lastPk
})
});
return await res;
} catch (error) {
console.log(error);
}
return null;
}


/*---- image method ----*/
getUserImage(pk = pkey) {
return this.apiurl + 'get-single-profile-picture/' + pk;
}


/*---- control methods ----*/

async getFrom(method, params = {}) {
let res;

switch (method) {
case 'get-app-state':
res = await this.getAppState();
console.log(res);
break;

case 'get-notifications':
res = await this.getNotificationsByPublicKey({});
console.log(res);
break;

case 'get-users':
res = await this.getUsers({});
console.log(res);
break;

case 'get-user-profile':
res = await this.getUserProfileByPublicKey({});
console.log(res);
break;

case 'get-user-profiles':
res = await this.getUserProfiles({});
console.log(res);
break;

case 'get-posts':
/* for Posts, pass:
pk: reader's public key (optional),
postHashHex: for paging, send last received post hash,
orderBy: newest|oldest|last_comment,
postContent: only return posts whose (body only?) matches the text,
numToFetch: non-zero,
fetchSubComments: return 1 level deep of comments? haven't tested yet,
getPostsForFollowFeed: follow feed for reader's pk,
getPostsForGlobalWhitelist: global posts,,
getPostsByDESO: kinda basically the hot feed i guess,
mediaRequired: false, or true but would be nice to specify image and/or video
postsByDESOMinutesLookback: must be <= 60, and only when doing getPostsByDESO,
addGlobalFeedBool: posts will contain a bool for isglobal (and i believe it controls inhotfeed also)
*/

try {
const batch = await this.getPosts({ pk: params.readerPk ? params.readerPk : undefined, postHashHex: params.postHashHex ? params.postHashHex : undefined, orderBy: params.orderBy ? params.orderBy : undefined, postContent: params.postContent ? params.postContent : undefined, numToFetch: params.numToFetch ? params.numToFetch : 10, mediaRequired: params.mediaRequired ? params.mediaRequired : false });
res = batch.PostsFound.filter(item => !blacklistedUsers.includes(item.PosterPublicKeyBase58Check));
} catch (error) {
res = [];
}
break;

case 'get-posts-all':
/* see 'get-posts' above
*/

try {

let batch = null;
let results = [];
let ct = 0;
const maxCalls = params.maxCalls ? params.maxCalls : 10;

let lastPostHash = params.postHashHex ? params.postHashHex : undefined;

while (ct < maxCalls && (batch === null || batch.length)) {
ct++;
batch = await this.getPosts({ pk: params.readerPk ? params.readerPk : undefined, postHashHex: lastPostHash, orderBy: params.orderBy ? params.orderBy : undefined, postContent: params.postContent ? params.postContent : undefined, fetchSubComments: params.fetchSubComments ? params.fetchSubComments : false, numToFetch: params.numToFetch ? params.numToFetch : 50, mediaRequired: params.mediaRequired ? params.mediaRequired : false });

batch = batch.PostsFound;

//console.log(batch.length);

if (batch.length) {
const filteredBatch = batch.filter(item => (!blacklistedUsers.includes(item.PosterPublicKeyBase58Check)) && item.Body.length >= (params.minBodyText ? params.minBodyText : 0) && item.Body.length <= (params.maxBodyText ? params.maxBodyText : item.Body.length));

//console.log(ct);
//console.log(filteredBatch.length);
//console.log(lastPostHash);

lastPostHash = '';
if (filteredBatch.length) {
results.push(...filteredBatch);
}

lastPostHash = batch[batch.length - 1].PostHashHex;
}
}

if (fs) {
fs.writeFile('./src/scripts/dataStore/' + lastPostHash + '.json', JSON.stringify(results), (err) => {
if (err) { console.error(err); return; };
console.log("File has been created");
});
}

res = results;
} catch (error) {
console.log(error);
res = [];
}
break;

case 'get-post':
/* for Posts, pass:
postHashHex: the post to retrieve,
pk: reader's public key (optional),
fetchParents: bool, returns all parents up to 100,
commentOffset: starting point for comments,
commentLimit: max # comments,
addGlobalFeedBool: posts will contain a bool for isglobal (and i believe it controls inhotfeed also)
*/

res = await this.getPostByPostHashHex({ pk: params.pk ? params.pk : undefined, postHashHex: params.postHashHex ? params.postHashHex : undefined, commentOffset: params.commentOffset ? params.commentOffset : undefined, commentLimit: params.commentLimit ? params.commentLimit : undefined, fetchParents: params.fetchParents ? params.fetchParents : undefined, addGlobalFeedBool: params.addGlobalFeedBool ? params.addGlobalFeedBool : undefined });
res = res.PostFound;
//console.log(res);
break;

case 'get-comments':
/* see 'get-post' above

this will get all comments and return them in the sequential order they should be displayed in, with a depth property
*/


let totalres = [];

let depth = params.depth ? params.depth : 0;

try {
res = await this.getPostByPostHashHex({ pk: params.pk ? params.pk : undefined, postHashHex: params.postHashHex ? params.postHashHex : undefined, commentOffset: params.commentOffset ? params.commentOffset : undefined, commentLimit: params.commentLimit ? params.commentLimit : undefined, fetchParents: params.fetchParents ? params.fetchParents : undefined, addGlobalFeedBool: params.addGlobalFeedBool ? params.addGlobalFeedBool : undefined });
if (res && res.PostFound && res.PostFound.Comments) res = res.PostFound.Comments;
else res = [];
for (let i = 0; i < res.length; i++) {
res[i].Depth = depth;
totalres.push(res[i]);
const subres = await this.getFrom('get-comments', { postHashHex: res[i].PostHashHex, commentLimit: 200, depth: depth + 1 });
if (subres.length)
totalres = totalres.concat(subres);
}
res = totalres;
} catch (error) {
console.log(error);
return null;
}


//console.log(res);
break;

case 'get-posts-by-user':
/* for Posts By User (for User), pass:
pk: public key
userName: user
readerPk: reader's public key
numToFetch: non-zero
mediaRequired: false
lastPostHashHex: for paging, send last received post hash
*/

res = await this.getPostsForPublicKey({ pk: params.pk ? params.pk : undefined, userName: params.userName ? params.userName : undefined, readerPk: params.readerPk ? params.readerPk : undefined, numToFetch: params.numToFetch ? params.numToFetch : 10, mediaRequired: params.mediaRequired ? params.mediaRequired : false, lastPostHashHex: params.lastPostHashHex ? params.lastPostHashHex : undefined });
break;

case 'get-likes-by-post':
res = await this.getLikesByPostHashHex({});
console.log(res);
break;

case 'get-reclouts-by-post':
res = await this.getRecloutsByPostHashHex({});
console.log(res);
break;

case 'get-quotes-by-post':
res = await this.getQuotesByPostHashHex({});
console.log(res);
break;

case 'get-diamonds-by-post':
res = await this.getDiamondsByPostHashHex({});
console.log(res);
break;

case 'get-hodlers':
res = await this.getHodlersByPublicKey({});
console.log(res);
break;

case 'get-user-diamonds-received':
/* for Diamonds Received (for User), pass:
pk: user's public key
fetchYouDiamonded: false
*/

res = await this.getDiamondsByPublicKey({ pk: params.pk ? params.pk : undefined, fetchYouDiamonded: false });
res.UniqueUsers = Object.keys(res.DiamondSenderSummaryResponses).length;
break;

case 'get-user-diamonds-sent':
/* for Diamonds Sent (for User), pass:
pk: user's public key
fetchYouDiamonded: true
*/

res = await this.getDiamondsByPublicKey({ pk: params.pk ? params.pk : undefined, fetchYouDiamonded: true });
res.UniqueUsers = Object.keys(res.DiamondSenderSummaryResponses).length;
break;

case 'get-user-num-followers':
/* for Number of Followers (for User), pass:
userName: the user's name
pk: empty string
getFollowersOf: true
numToFetch: 0
lastPk: "" (LastPublicKeyBase58Check)
*/

res = await this.getFollowsByPublicKey({ pk: params.pk ? params.pk : undefined, userName: params.userName ? params.userName : undefined, getFollowersOf: true, numToFetch: 0, lastPk: "" });
res = res.NumFollowers;
break;

case 'get-user-num-following':
/* for Number of Following (for User), pass:
userName: the user's name
PublicKeyBase58Check: empty string
getFollowersOf: false
numToFetch: 0
lastPk: "" (LastPublicKeyBase58Check)
*/

res = await this.getFollowsByPublicKey({ pk: params.pk ? params.pk : undefined, userName: params.userName ? params.userName : undefined, getFollowersOf: false, numToFetch: 0, lastPk: "" });
res = res.NumFollowers;
break;

case 'get-user-follow-counts':
const res_followers = await this.getFrom('get-user-num-followers', { pk: params.pk ? params.pk : undefined, userName: params.userName ? params.userName : undefined });
const res_following = await this.getFrom('get-user-num-following', { pk: params.pk ? params.pk : undefined, userName: params.userName ? params.userName : undefined });

res = new Object();
res.num_followers = res_followers;
res.num_following = res_following;

break;

case 'get-user-followers':
/* for Users (who are) Followers (for User), pass:
userName: the user's name
PublicKeyBase58Check: empty string
getFollowersOf: true
numToFetch: non-zero
lastPk: public key (where to start next page at)

example code shows that the list might need to be sorted by DeSoLockedNanos before
this would work as expected
*/

res = await this.getFollowsByPublicKey({ pk: params.pk ? params.pk : undefined, userName: params.userName ? params.userName : undefined, getFollowersOf: true, numToFetch: params.numToFetch ? params.numToFetch : 10, lastPk: params.lastPk ? params.lastPk : undefined });
res.PageCount = Object.keys(res.PublicKeyToProfileEntry).length;
res.sortedProfiles = Object.entries(res.PublicKeyToProfileEntry)
.sort((ii, jj) => jj[1].CoinEntry.DeSoLockedNanos - ii[1].CoinEntry.DeSoLockedNanos);
res.SortedPageCount = res.sortedProfiles.length; // not currently filtering any data out, so it will be equal to PageCount
res.lastPk = res.sortedProfiles[res.SortedPageCount - 1][1].PublicKeyBase58Check;
break;

case 'get-user-followings':
/* for Users (who are) Followings/Followed (for User), pass:
userName: the user's name
PublicKeyBase58Check: empty string
getFollowersOf: false
numToFetch: non-zero
lastPk: public key (where to start next page at)

example code shows that the list might need to be sorted by DeSoLockedNanos before
this would work as expected
*/

res = await this.getFollowsByPublicKey({ pk: params.pk ? params.pk : undefined, userName: params.userName ? params.userName : undefined, getFollowersOf: false, numToFetch: params.numToFetch ? params.numToFetch : 10, lastPk: params.lastPk ? params.lastPk : undefined });
res.PageCount = Object.keys(res.PublicKeyToProfileEntry).length;
res.sortedProfiles = Object.entries(res.PublicKeyToProfileEntry)
.sort((ii, jj) => jj[1].CoinEntry.DeSoLockedNanos - ii[1].CoinEntry.DeSoLockedNanos);
res.SortedPageCount = res.sortedProfiles.length; // not currently filtering any data out, so it will be equal to PageCount
res.lastPk = res.sortedProfiles[res.SortedPageCount - 1][1].PublicKeyBase58Check;
break;

}

return res;

}

}

module.exports = DesoLite;