Подробная инструкция по созданию бота-куратора работающего в браузере
Выложенный вчера скрипт принес некоторое количество вопросов в комментариях, в том числе по части устройства и работы бота. Как и обещал - выкладываю подробнейшее описание каждой функции, надеюсь это поможет вам в реализации собственных идей
Демо: https://golos.rubtc.info/bot.html
Из инструментов нужен только браузер и текстовый редактор с поддержкой html и js
Создайте у себя папку с произвольным именем, далее создайте в ней два файла:
bot.html
и golos.js
например используя notepad++ или другой текстовый редактор откройте оба файла.
В файл bot.html
вам необходимо вставить html со страницы
http://pastebin.com/raw/VgimfAwC
А в файл golos.js
вставьте код со страницы
https://raw.githubusercontent.com/ontofractal/golosjs/master/dist/golos.min.js
Теперь можете открыть страницу bot.html у себя в браузере и используя форму задать скрипту условия алгоритма действий, он будет работать. Понять как именно все работает я помогу ниже.
Шаблон страницы
В основе у нас минимальная html5 разметка, мы не будем уделять ей много времени, об этом в сети очень много информации, это одна из основ web.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>BotBro</title>
<style></style>
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<script src="golos.js"></script>
</head>
<body>
<script></script>
</body>
</html>
meta name="viewport"
не обязательный параметр, он нужен для удобного отображения страницы на маленьких дисплеях, телефон, планшет и т.д.
<script src="golos.js"></script>
- Это у нас подключенный javascript помогающий подключаться к блокчейну голоса. Скрипт в редакции @ontofractal
<style></style>
Между этих тегов мы вставим наш CSS стиль (Опишем цвета, размеры, анимации и т.д.)
После тега <body>
мы добавим форму на 5 опций:
<form>
<input id="account" type="text" name="account" placeholder="Логин">
<input id="k" type="password" name="password" placeholder="Постинг ключ">
<input id="username" type="text" name="username" placeholder="Куратор - цель">
<input id="minutes" type="number" name="text" placeholder="Прошло минут">
<input id="votepower" type="number" name="text" placeholder="Сила от ...%">
</form>
- Ваш логин - ID поля формы
account
- Ваш постинг ключ - ID
k
- Логин цели, за которой повторить голоса - ID
username
- Количество прошедших минут, которые нужно учесть - ID
minutes
- Минимальная сила голоса с которой следует повторять - ID
votepower
В форме не хватает кнопки отправки, разместим ее ниже под формой
<button onclick="broBot()" class="signin">Запуск голосования</button>
И добавим еще один блок в котором будет отражаться ход голосования
<div id="nicedata"> </div>
JavaScript
Перед нижним </body>
мы разместим между тегами <script> </script>
наш скрипт, который будет выполнять всю работу.
И начнем мы с указаний переменных и написания основной функции
Обратите внимание на форму, которая описана ранее, каждое из полей ввода имеет свое ID, а кнопка
button onclick="broBot()"
вызывает функцию broBot
при клике.
Таким образом, весь код внутри функции broBot()
будет выполнен только при нажатии кнопки.
За пределами функции у нас объявлена только одна глобальная переменная:
var votepower = 0;
- это значит, что если вы оставите поле с id votepower пустым, то минимальная сила голоса будет начинаться от нуля. И бот будет повторять все голоса куратора не меньше 0.
Если вы зададите в поле формы 50 (например), то бот будет игнорировать голоса менее 50% силы, а повторять только голоса, в которых куратор указал силу от 50%.
После объявления этой переменной, создадим основную функцию
function broBot() {
// Тут будет остальной код
}
И уже внутри функции продолжаем задавать переменные для 5-ти полей формы
var account = document.getElementById("account").value,
k = document.getElementById("k").value,
username = document.getElementById("username").value,
minutes = document.getElementById("minutes").value,
votepower = document.getElementById("votepower").value;
В каждую из переменных попадает значение с поля формы благодаря функции
document.getElementById("ID-элемента").value
Так как изначально форма не заполнена, а переменные находятся внутри функции broBot()
- заполнение произойдет после нажатия кнопки.
Далее у нас будет еще одна функция внутри broBot()
, назовем ее followVote()
, но перед этим объявим еще несколько глобальных переменных для того, что бы могли в будущем влиять на их содержимое изнутри других функций
var time,
starttime,
utime,
start,
t = 1000, // Просто сокращение.
period = minutes * 60; // Переводим указанные в поле минуты в формат unix time
Начинаем писать после followVote(){
var count = true; // переменная - выключатель.
steem.api.getDynamicGlobalProperties(function(err, result) {
starttime = Date.parse(result.time) / t;
});
Тут произошло наше первое обращение к БЧ голоса, мы получили глобальные текущие данные, которые записали в ответ result
.
Из всего этого нам нужно знать только текущее время указанное в БЧ и записать его в переменную starttime
.
Выбираем time из объекта так: result.time
, что даст нам такой формат "2017-02-17T18:37:24". Такой формат удобно читать человеку, но не удобно оперировать как математической единицей, мы переведем его unix time (общепринятый формат времени, который исчисляется как количество секунд прошедших с начала эпохи юникс - 1970 год)
Date.parse(result.time) / t;
- если помните, t у нас равняется 1000, т.е. unix time мы получили поделив результат функции Date.parse на 1000, что дало нам 1487356644 секунд. Именно в таком формате мы будем записывать стартовое время нажатия кнопки запуска бота.
Это время будет храниться в переменной starttime
и обновляться на текущее с каждым нажатием кнопки запуска.
Сканируем ВСЕ голоса куратора-цели
Было бы логичней сканировать не все, а только часть голосов с лимитом по дате или количеству, но в нынешнем api объекты внутри массива отсортированы странным образом, потому мы будем загружать всю историю голосования куратора, а уже дальше сортировать его голоса по "давности".
Сперва мы делаем запрос в БЧ голоса
steem.api.getAccountVotes(username, function(err, result) {
username
- это переменная, в которой хранится имя куратора, которое мы задали в поле формы с одноименным id.
Делаем заготовку для массива, в который мы вставим полученные объекты
var a = [];
Обращение к api с запросом getAccountVotes выдаст нам массив объектов с данными о исходящих голосах username
Каждый голос пронумерован, однако, нумерация идет странным порядком, в который стоило бы вникнуть, но было лень) Суть в том, что последний голос не имеет последний порядковый номер или ближайшую дату. Они как-то вперемешку... Но нам точно известно их количество благодаря length (видно на скрине). Это число можно получить обратившись к result.length
внутри getAccountVotes
Мы сделаем вот такую петлю:
for (var i = 0; i < result.length; i++) {
// Здесь мы будем формировать список всех исходящих голосов
}
В этой петле выведется содержимое каждого из 443 объекта (в нашем случае)
Например содержимое моего 400-го голоса выглядит вот так
Запомните, что i
для каждой строки будет равно своему порядковому номеру. От нуля до общего количества голосов length.
Пропишем еще некоторые переменные
var arr = result,
Все содержимое объектов теперь в arr.
Зададим время старта для бота задним числом. Например собрать голоса выбранного куратора за прошедший час.
start = starttime - period,
- Если помните, в начале скрипта
startime
содержит время запуска бота period
у нас =minutes * 60
minutes
у нас = задаваемое вами значение в минутах в поле формы с одноименным ID.
Таким образом в переменную start мы запишем указанные вами минуты переведенные в формат секунд (unix time). Поскольку starttime у нас равно сейчас 1487356644
- вычтем из него 60 минут (60min*60=3600sec) и мы получим
1487356644 - 3600 = 1487353044.
Это и будет наш старт для бота задним числом (час назад)
Для голосования нам нужно 3 значения логин, ссылка, сила.
Например, что бы проголосовать за мой пост, нужно
vik
, bot-bro-wser-ne-trebuyushii-servera-navykov
,1000
Но в нашем объекте все хранится так:
authorperm = vik/bot-bro-wser-ne-trebuyushii-servera-navykov
percent = 1000
Нам потребуется из этого объекта достать 3 отдельных значения. Ссылку на пост, логин автора и силу голоса в процентах X100. Но просто получить это в переменные не получится, так как логин склеен с ссылкой.
Решить это можно некоторыми манипуляциями в переменных
ap = arr[i].authorperm,
Выше мы записали в переменную ap
нашу строку вида vik/bot-bro-wser-ne-trebuyushii-...
И теперь в переменную author
запишем только логин из строки. Для этого обрежем все начиная с /
и оставит только то, что до косой.
author = ap.substring(0, ap.indexOf('/')), // Теперь тут vik
Почти то же сделаем с ссылкой на пост. Обрежем все до /
и оставим только текст после /
permlink = ap.substring(ap.indexOf('/')).substring(1), // Тут ссылка на пост
Логин и ссылку получили, теперь запишем в переменные силу, и время совершения апвота.
power = arr[i].percent, // 1000 для 100%, или 500 для 50% и т.д.
time = arr[i].time; // Время в человеческом формате
utime = Date.parse(time) / t; // Переводим в unixtime
Теперь, когда наши переменные с автором, ссылкой и силой готовы для каждого голоса для username, мы воспользуемся благом ставить лимит на срок давности голоса который хотим повторить.
Создаем условие если : utime
больше start
.
- utime - дата на каждом совершенном голосе
- start - заданное нами отправное время для бота
if (utime > start) {
// Тут будет массив с переменными
}
Теперь внутри условия будет заполняться массив a
, который мы объявили чуть раньше var a = [];
a.push({
author: author, // vik
permlink: permlink, // Ссылка на пост
power: power, // 1000
utime: utime, // не обязательно
start: start // не обязательно
});
Все заполнено и мы можем обратиться к массиву a
за данными о голосах username, только тех которые позднее нашего времени в переменной start.
Теперь у нас своего рода список голосов за некоторый период и мы хотим за все проголосовать.
Если мы пошлем в голос запрос на голосование за все посты разом - фактически проголосовать удастся лишь за один пост, так как действует ограничение в blockchain - только один голос за 3 секунды. Стало быть нам нужно взять наш список, порой внушительный, и заставить бот голосовать по одному голосу с интервалом в 3 секунды. Есть у нас решение и для этого
Объявим 2 переменные.
summ
- будем хранить число - количество голосов, которые нужно повторить.
i
- будет нулем
var summ = a.length;
var i = 0;
Теперь создадим функцию в переменной goVote
var goVote = setInterval(function() {
// Все что внутри, выполняется с интервалом в 3 секунды
}, 3000);
Внутрь поместим условия
if (count && summ > 0 && a[i].power / 100 >= votepower) {
}
Если count = true
(вкл), а оно у нас true в самом вверху и будет таковым, пока мы сами не переключим в false(выкл). И если summ
больше 0
- то есть если наш полученный массив a
содержит голоса. Ведь может и не содержать! Например вы выбрали глубину в 30 минут. А куратор голосовал последний раз еще вчера - не будет голосов тогда.
И наконец a[i].power / 100
больше или равно переменной которая есть поле с id votepower
в форме вверху. В поле мы указываем силу голоса вида 100% а голосуем в формате, где 100% = 1000. Потому делим нашу силу голоса на 100. Только для того, что бы вам было удобнее задавать ее в форме.
Голосование
steem.broadcast.vote(k, account, a[i].author, a[i].permlink, a[i].power, function(err, result) {
console.log(err,result);
});
Передаваемые параметры в блокчейн это
k
- Постинг ключ владельца бота
account
- логин владельца бота
a[i].author
- логин куратора за которым следуем
a[i].permlink
- ссылка на пост
a[i].power
- сила
Обратили внимание на i
? Мы задали выше, что i = 0, наш список целевых голосов в массиве a
начинается с 0
(первый голос это не 1, это 0. Особенности array и object в js).
Так же помним, что мы в 3-х секундном интервале.
Добавляем в код внутри интервала возрастающую при каждом запуске переменную:
i++;
Теперь, у нас с каждым циклом в 3 секунды переменная i увеличится на 1.
Первый запуск выдернет из массива a
первый голос
a[0].author a[0].permlink a[0].power
Второй запуск выдернет второй голос из массива
a[1].author a[1].permlink a[1].power
И так далее:
a[2].author a[2].permlink a[2].power
Таким образом с интервалом в 3 секунды мы перебираем строку за строкой в списке голосов username. Перебираем и отправляем голос
steem.broadcast.vote(k, account, a[i].author, a[i].permlink, a[i].power, function());
Можем закрыть первое условие...
В этом месте у меня еще необязательная косметическая функция
itemShow()
- делает появление голосов анимированным.
Назад в будущее. Второе условие.
Мы все еще не будем закрывать скобки нашего интервала и напишем второе условие внутри 3-х секундного интервала:
if (i == summ) {
count = false;
period = 4;
clearInterval(goVote);
followVote();
}
Если i
- оно же номер голоса равно summ
- сумма голосов в массиве a
, то значит, что мы перебрали весь массив и нам нужно остановить слать голоса в никуда.
Внутри условия произойдет следующее
Мы меняем в переменной count
значение true
на false
, что будет значить, что мы запретим теперь выполнять предыдущее условие if (count)
в котором функция отправки голоса. Запрещаем по причине того, что список голосов уже обработан ботом.
Так же мы сбрасываем указанное start
время до времени настоящего минус 4 секунды.
period = 4;
Таким образом, бот отработал указанное вами "прошлое" куратора, начинает отрабатывать настоящее.
Так же мы сбрасываем (отключаем) интервал goVote
.
И в завершение... Все по новой - мы снова запускаем 'followVote();' - что по сути своей делает почти тоже что и нажатие кнопки старта бота broBot()
, но с тем условием, что теперь глубина ретроспективы равна не тому, что у вас в поле формы, а 4 секундам, так как заданное в форме уже было обработано и нет смысла начинать по новой с того старого периода. Теперь бот работает с тем же интервалом в 3 секунды и ждет новых голосов от вашей цели.
Косметические функции
Внутри интервала голосования мы не только посылаем голоса, но и выводим-отображаем список этих голосов. В переменную votehtml
мы поместили логин, ссылку на пост и силу голоса (тысячи правильно поделили на проценты)
votehtml = '<div id="item" class="myJson"><a href="https://golos.io/@' + a[i].author + '"><strong>' + a[i].author + '</strong> ' + a[i].permlink + ' <i>' + a[i].power / 100 + '%</i> </a></div>';
Далее мы выводим переменную в блок c id nicedata
document.getElementById('nicedata').insertAdjacentHTML('afterbegin', votehtml);
При этом в css у нас эти элементы описаны так:
#item{
transition:1s all ease;
transform:translate3d(0px, -100px, 0px);
position:absolute;
opacity:0;
}
Изначально они появляются невидимыми и на 100px выше. Но в наше интервале есть функция itemShow()
которая добавляет элементам с id item класс anim
function itemShow() {
setTimeout(function() {
document.getElementById("item").classList.add('anim');
}, 200);
}
А класс anim
у нас уже описан в CSS иначе
#item.anim{
transform:translate3d(0px, 0px, 0px);
opacity:1;
transition:1s all ease;
position:relative;
}
Плавно, с transition:1s all ease;
спустились на 100px в нормальное положение и прозрачность.
Весь css можно посмотреть в исходнике
http://pastebin.com/raw/VgimfAwC
Охапку дров и плов бот готов :)
Прошлые посты по теме: