⛓ Снятие лимитов в популярной golos/steem JS библиотеке на примере фан-карты Mapala
Голос стал песочницей для множества начинающих разработчиков и просто любителей.
Блокчейн с открытым кодом + дружное сообщество - все это делает голос отличной площадкой для приобретения и прокачки разных навыков взаимодействия с трендовыми технологиями.
В этом посте я приведу пример, который поможет снять некоторые "мнимые" ограничения в API golos/steem
Надеюсь это поможет всем, кому нравится созидать что-то на блокчейне голоса.
Речь пойдет о steem-js она же golos-js и других форках.
Как минимум мы сможем выйти за рамки лимита в 100 постов в API для вызова getDiscussions
Мне это понадобилось в процессе разработки телеграм-бота и фан-карты для mapala.
На карту выводятся все существующие посты с MAPALA в которых есть географические координаты:
Посмотреть вживую можно тут https://chain.cf/mapala - это тестовый пример и весь скрипт выполняется в браузере,посты грузятся достаточно долго. Но при использовании рендера не сервере - это дело нескольких секунд. Я не зря назвал это фан-картой - так как акцентирую, что я не состою в команде разработчиков. То что делаю, это не коммерческие продукты, это скорее просто хобби.
В телеграм боте мне так же понадобилась возможность выводить все посты без ограничений в 100, при этом давая возможность прочесть контент и посмотреть фото прямо в боте.
Над этими и еще над с 10-к проектов идет работа, многое не получается успеть просто физически, а большая часть времени уходит на изучение API и ответы пользователям в тг, надеюсь и буду рад, если мои наработки и потраченное время сэкономят ресурсы таким же энтузиастам :)
Приступим!
Нам вместе с библиотеками golos-js/steem-js поставляется неполная инструкция к вызовам вида:
Get Discussions By Created
golos.api.getDiscussionsByCreated(query, function(err, result) {
console.log(err, result);
});
Здесь и далее будем рассматривать этот API вызов в качестве подопытного.
Вызов getDiscussionsByCreated
- возвращает нам список постов сортированных по дате создания, а query это конкретный запрос диктующий, что именно возвращать. Проблема в том, что на официальной странице либы нет описания того, что нужно вставлять в query
и начинающий разработчик-любитель полезет в документацию и увидит что в query
должен быть тег и лимит {"tag":"mapala","limit":100}
Скрин со страницы документации https://developers.golos.io/doc/client#discussions
Кроме официальной документации это так же указано в API explorer
Более того, если мы хотим вывести список постов в количестве большем чем 100, то мы получим ошибку.
Например если мой query будет {"select_tags":["mapala"],"limit":101}
, то я не получу 101 пост, а получу ошибку
Представьте, что нам нужно выводить подгружаемую ленту постов, эдакий infinity scroll на странице.
Для этого делаем запрос с пустым тегом (это выведет все посты,независимо от тегов):
get_discussions_by_created({"select_tags":["mapala"],"limit":100})
В качестве ответа мы получили максимум 100 постов и плюс ко всему, в этих 100 постах у нас 80 от bm, которые мы скроем, в остатке останется каких-то 20 постов с голоса без возможности подгрузить более ранние...
Долгое время я верил этому искусственному лимиту и он меня порядком раздражал.
Но как оказывается, проблема не в лимитах, а в неполной и недостаточной документации для вызовов.
Поражает, что есть полно не задокументированных возможностей!
картинку украл в комментах у Дена Ларимера :)
Вглянем еще раз на скрин ошибки
Сообщение о лимите api нам показывает файл database_api.hpp
. Так как мы работаем с открытом кодом (и это замечательно!) мы можем найти содержимое этого файла в исходниках голоса.
hpp
- это расширение для языка C++, идем в репозиторий голоса на github и находим там исходники на C++
Вышеупомянутый файл можно найти по пути /golos/libraries/app/database_api.cpp
Ищем запрос _created
и находим его на строке 1467
https://github.com/GolosChain/golos/blob/master/libraries/app/database_api.cpp#L1467
Я не стану приводить весь код, он достаточно объемный, нам нужно изучить строки с 1467 по 1520, что бы узнать, что лимит в js плагинах был искусственный.
Какое на самом деле доступно и может быть query для getDiscussionsByCreated
Кроме всюду упомянутых tag и лимит нам так же доступно вот что:
select_authors
Мы можем указать список авторов
select_tags
Мы можем указать сет тегов, а не только один!
filter_tags
Мы можем убрать из выдачи ненужные теги, например bm-open!
truncate_body
Мы можем сократить контент до короткого описания - например для вывода карточек постов с кратким описанием.
parent_author
Автор-родитель - будут выводится все ответы автору!
parent_permlink
Родительская ссылка - все ответы к определенному посту
start_author и start_permlink
А вот эти 2 параметра помогут сделать offset (отступ) для постов! Это именно то, что мне нужно было - возможность сделать отступ и загружать последовательно все посты на голосе забыв про лимит в 100 постов! Как именно это сделать я напишу ниже.
Бесконечный скролл всех постов голоса
Немного усложним задачу и выведем все посты с mapala в которых есть координаты местности (в клиенте мапала при размещении поста можно указать широту и долготу местности)
Создаем promise и загружаем в posts первые 100 постов с тегом mapala
const POSTCOUNT = 0
const item = []
const LATEST_POSTS = new Promise((resolve, reject) => {
golos.api.getDiscussionsByCreated({"select_tags":["mapala"],"limit":100},(err, posts) => {
if (err) {
reject(err)
}
else {
resolve(posts)
}
})
})
Передаем posts в следующую функцию, где задаем пустой массив nextset
const POSTS_FILTER = (posts) => {
let nextset = {
start_permlink:'',
start_author:''
}
Укажем количество постов которые получили в ответе
let len = posts.length
Создадим цикл для каждого полученного поста
for (let post of posts) {
В переменную meta будем сохранять json_metadata для каждого раунда цикла
let meta = JSON.parse(post.json_metadata)
Теперь создадим условие, с помощью которого будем обрабатывать только те посты, в которых указаны координаты местности
if(typeof meta.coordinates !=="undefined" && meta.coordinates.length > 10 ){
Внутри условия мы создадим массив в который будем толкать различные данные постов
item.push({
author:post.author,
parent:post.parent_permlink,
title:post.title,
body:post.body.replace(/<\/?[^>]+(>|$)/g, ""),
image:(typeof meta.image === 'undefined')?'/logo.png':meta.image[0],
payout:post.total_pending_payout_value,
url:'https://golos.io'+post.url,
meta:meta,
time:post.created,
geo:meta.coordinates
});
И после, когда мы будем на этапе обработки последнего поста в цикле мы заполним массив nextset автором и ссылкой на последний пост
if(i == len - 1){
nextset.start_permlink = post.permlink;
nextset.start_author = post.author;
NEXT_POSTS(nextset)
}
Это именно та уловка с помощью которой мы получаем возможность получить следующие 100 постов!
Сначала мы получили 100 первых постов, отфильтровали их по вкусу и данным, запомнили автора и ссылку на последний пост и теперь можем вызвать серию из 100 следующих постов задав полученные автора и ссылку как старт.
Вот так:
const NEXT_POSTS = (nextset) => {
golos.api.getDiscussionsByCreated({
"start_permlink":nextset.start_permlink,
"start_author":nextset.start_author,
"select_tags":["mapala"],"limit":100}
Полученные следующие 100 постов обрабатываем так же как и в предыдущем примере, затем снова сохраняем последнего автора и ссылку и с этим данными снова рекурсивно вызываем функцию NEXT_POSTS
nextset.start_permlink = post.permlink;
nextset.start_author = post.author;
if(len > 1){
NEXT_POSTS(nextset)
}
Условие if(len > 1)
помогает нам перестать вызывать функцию если мы уже получили абсолютно все посты.
Код полностью выглядит так
Вместо PS и вместо отчета делегата.
Народ требует отчетов, но мне просто иногда жаль засирать незыблемость блокчейна очередным отчетом, потому буду краток :)
Хочу напомнить, что вчера вместе с @ropox и @everythink мы запустили @dobrobot - бота который делает добро за ваш счет :) Вы пополняете добробота, а он раздает ваши монеты тем, за кого вы голосуете. Легкий способ почувствовать себя одаривающим китом :)
💱Подробнее
Так же напомню, что моя собственная инициатива @robot так же активно работает и делает добро, но уже за мой счет :) Не забывайте рекомендовать авторов, которым @robot будет оказывать финансовую поддержку.
🎁Подробнее
Кроме этого я принял участие в 10-ке разных ICO в т.ч. #mapala - ребята делают большую работу и скоро она будет показана публике, а сейчас есть возможность приобрести их токены по минимально возможной цене. Можно с уверенностью сказать, что у них все только начинается.
Про golos.io я так же не забыл, это можно видеть из финансовой статистики @arcange
Я в 10-ке пользователей больше всех инвестировавших в силу голоса. (Про голос я узнал только в январе, потому в ICO не участвовал)
К слову - из голоса я еще ни разу не выводил какие-либо средства :)
Из печального...
1. Это, как ни странно то, что я в вышеупомянутой 10-ке... Мне казалось что передо мной будет гораздо больше пользователей. Хотелось бы быть со своими копейками в хвосте.
2. Вторая печаль - это внезапный отказ достаточно дорогого сервера на котором была нода и десяток телеграм ботов которыми активно пользуется народ.
После чего я сразу поднял резервную ноду в другом датацентре - работа была восстановлена в кратчайшие сроки.
На связи
Я почти всегда доступен для обратной связи, признаться на это уходит очень много времени, так как стараюсь отвечать всем.
https://t.me/chain_cf - чат посвященный @robot и тг ботам для голоса
https://t.me/vikxx - личные сообщения
https://chat.golos.io/direct/vik - личные сообщения
https://discord.gg/k6TMZx6 - чат буржуйский.