Разработка персональных ботов для Голоса. Урок 1.
В этом уроке мы научимся получать и обрабатывать информацию о новых блоках Голоса, генерируемых делегатами. Для этого мы напишем простой код на JavaScript, который будет выполняться на компьютере пользователя с помощью nodejs.
Предисловие
- Требуется базовый уровень понимания JavaScript, веб технологий и командной строки
- Задавайте вопросы в комментариях, если я что-то непонятно объясняю.
- В этом уроке мы создадим основу для разных ботов. Учиться автоматически реагировать на события блокчейна мы будем в следующих уроках.
- У меня минимальный опыт работы с русскоязычной терминологией в программировании, поэтому названия я буду оставлять на английском языке.
- Несмотря на то, что на бекэнде я предпочитаю использовать Elixir, для первых уроков гораздо лучше подойдет JavaScript.
- В этом уроке используется неоптимальный код, паттерны и структура, приоритизируется легкость для понимания и читаемость кода.
Зачем нужны боты?
В данном случае решается несколько целей, одна из них -- дать возможность аккаунтам следовать за голосами @academy без предоставления мне или кому-то другому постинг ключа.
Подготовка и выбор технологического стека
Что такое стек? Буквально "stack" означает стопка и используется для обозначения комплекса языков, технологий и приложений.
Разрабатывать программу бота будем на JavaScript. Если точнее, то на JavaScript 2016, а если еще точнее, то на версии JavaScript, которую поддерживает последняя версия nodejs.
Вначале была среда программирования. Для ее развертывания нам необходимо выполнить следующие действия:
- установить последнюю версию node.js (изучите тут )
- инициализировать новый проект с помощью
npm init
(показано в скринкасте) - установить мой форк npm модуля:
"golos":"ontofractal/golosjs"
в package.json ( в скринкасте ошибочная версия npm модуля golos)
Скринкаст подготовки нового проекта
Первые строки
- Для упрощения управлением асинхронным потоком данных используем Promises
- node.js использует собственный формат модулей, в будущих релизах node.js (и браузеров) система импортирования модулей будет заменена на нативные EcmaScript 2016 модули, которые выглядят вот так:
import golos from 'golos'
- Библиотека
golos
использует вебсокеты и протокол JSONRPC для обмена данными с нодами Голоса. По умолчанию библиотека подключается к публичной нодеwss://ws.golos.io
// импортируем модуль голоса
const golos = require('golos')
// импортируем модуль Bluebird -- самую популярную имплементацию Promise
const Promise = require("bluebird")
// аккаунт пользователя, который запускает бота
const accountName = ''
// приватный постинг ключ пользователя, который запускает бота
const postingKey = ''
// аккаунт пользователя за которым следим
const accountNameToFollow = 'academy'
// создаем новый Promise c помощью обвертывания golos.api.getDynamicGlobalProperties
const dynamicGlobalProperties = new Promise((resolve, reject) => {
golos.api.getDynamicGlobalProperties((err, result) => {
if (err) {
reject(err)
}
else {
resolve(result)
}
})
})
// резолвим Promise
dynamicGlobalProperties
.then(x => console.log(x))
.catch(e => console.log(e))
Создаем функцию, которая выдернет из полученного от ноды объекта глобальных свойств номер последнего блока
const pluckBlockHeight = x => x.head_block_number
Создаем функцию, которая запрашивает данные блока
const getBlockData = height => {
golos.api.getBlock(height, (err, result) => {
if (err) {
console.log(err)
}
else {
console.log(result)
}
})
}
Скринкаст результата
Создаем функцию, которая будет запрашивать данные следующего блока с регулярным интервалом
const startFetchingBlocks = startingHeight => {
let height = startingHeight
setInterval(() => {
getBlockData(height)
height = height + 1 // брррр, мутация
// у нас есть доступ к переменной height благодаря closure
}, 3000)
// Задаем интервал в 3000 мс т.к. блок Голоса генерируется каждые три секунды
}
Сводим все вместе
const golos = require('golos') // импортируем модуль голоса
const Promise = require("bluebird") // импортируем модуль Bluebird -- самую популярную имплементацию Promise
const accountName = '' // аккаунт пользователя, который запускает бота
const postingKey = '' // приватный постинг ключ пользователя, который запускает бота
const accountNameToFollow = 'academy' // аккаунт пользователя за которым следим
// создаем новый Promise обворачивая golos.api.getDynamicGlobalProperties
const dynamicGlobalProperties = new Promise((resolve, reject) => {
golos.api.getDynamicGlobalProperties((err, result) => {
if (err) {
reject(err)
}
else {
resolve(result)
}
})
})
const pluckBlockHeight = x => x.head_block_number
const getBlockData = height => {
golos.api.getBlock(height, (err, result) => {
if (err) {
console.log(err)
}
else {
console.log('')
console.log('============ НОВЫЙ БЛОК ============')
console.log(result)
}
})
}
const startFetchingBlocks = startingHeight => {
let height = startingHeight
setInterval(() => {
getBlockData(height)
height = height + 1 // брррр, мутация
// у нас есть доступ к переменной height благодаря closure
}, 3000)
// Задаем интервал в 3000 мс т.к. блок Голоса генерируется каждые три секунды
}
// резолвим Promise
dynamicGlobalProperties
.then(pluckBlockHeight)
.then(startFetchingBlocks)
.catch(e => console.log(e))
Получаем информацию о новых блоках Голоса
В следующем уроке
- Изучим структуру блоков и транзакций
- Научимся отличать одни транзакции от других
- Научимся обрабатывать поступающие данные о голосах пользователей