Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
vik
8 лет назад

Подробная инструкция по созданию бота-куратора работающего в браузере

Выложенный вчера скрипт принес некоторое количество вопросов в комментариях, в том числе по части устройства и работы бота. Как и обещал - выкладываю подробнейшее описание каждой функции, надеюсь это поможет вам в реализации собственных идей


Демо: 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

Охапку дров и плов бот готов :)


Прошлые посты по теме:

6
1389.211 GOLOS
На Golos с January 2017
Комментарии (36)
Сортировать по:
Сначала старые