Внутренности @gemini
Многие уже в курсе способностей сервисного робота @gemini работающего в ГОЛОСе.
Публикую код робота, только ту его часть, которая отвечает непосредственно за апвоуты по вызову пользователей (погодная часть из публикуемого кода изъята).
Робот работает на ноде. Часть его собрана по исходному коду @vik.
Я не программист, особенно в части ява. С ней я связался исключительно из-за любопытства и доступностью примеров опубликованных ранее @vik. Можно сказать, что этот робот был написан в третий присест за изучением Node.js
Внутри кода я написал комментарии. Если их недостаточно, то отвечу на вопросы.
Сменив ник робота в коде, постинг ключ, а так же ключевую фразу для активации робота - у Джемини появится брат-близнец.
const Promise = require("bluebird")
const _ = require('lodash')
const golos = require('steem')
golos.config.set('websocket','wss://ws.golos.io');
golos.config.set('address_prefix','GLS');
golos.config.set('chain_id','782a3039b478c839e4cb0c941ff4eaeb7df40bdd68bd441afd444b9da763de12');
//----------------------------
var k='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; //постинг ключ робота
var account='gemini'; //аккаунт робота
var percent=2500;//сила голоса апвоута (25%)
// фикс обработки несуществующих блоков
let trig = {
existBlock:true
}
// получение глобальных динамических данных
const dynamicSnap = new Promise((resolve, reject) => {
golos.api.getDynamicGlobalProperties((err, res) => {
if (err) {
console.log(err)
}
else {
resolve(res)
}
})
})
// получение номера последнего блока
const FIRSTBLOCK = n => n.head_block_number
// достаем операции из транзакций
const OPS = (ops) => {
return _.flatten(ops.transactions.map(tx => tx.operations))
}
// фильтруем операции
const OPSFILTER = (operation) => {
const [type, data] = operation
// определяем операцию размещения комментария
if (type === 'comment' && data.title==='') {
//определяем, что позвали нашего робота
if (data.body.substring(0,11)==="@gemini up!") {
$author=data.author; //определяем автора комментария
$permlink=data.permlink; //определяем пермлинк комментария
oncomm=level (); //определяем уровень комментария
//+------------------------------------------------------------------+
//| функция определения уровня комментария |
//+------------------------------------------------------------------+
function level (){
var lvl = $permlink; // пермлинк комментария
var target = "-re-"; // цель поиска в пермлинке
var pos = 2; //начинаем с третьей позиции (так как первые символы 're-' отличаются от цели)
var count=0; //начальный счётчик ставим в нуль
while (true) { //запускаем цикл поиска
var foundPos = lvl.indexOf(target, pos);//индекс найденой позиции цели
if (foundPos == -1) break; //если дошли до конца пермлинка, прекращаем цикл поиска
pos = foundPos + 1; //нашли цель, начинаем поиск со следующей позиции
count = pos-foundPos+count; //считаем количество найденых целей
if (count===4) return count; //если целей 4 штуки (5-й уровень комментария) значения счётчика определено, до этой цифры значение 'undefined'
}
}
//+------------------------------------------------------------------+
//пока значения уровня комментария не определено (робот может разместить ответный комментарий)
if (oncomm===undefined){
parentAuthorAns=data.parent_author; //определяем автора родительского комментария (или поста)
parentPermlinkAns=data.parent_permlink;//определяем пермлинк родительского комментария (или поста)
permlinkcomm='re-'+$author+'-'+$permlink;//формируем пермлинк ответного комментария робота
t=""; //титры (название) ответного комментария робота (пустое значение для комментариев)
jsonMetadata=data.json_metadata;//дублируем метадату json
//запрашиваем данные по контенту к которому оставлен комментарий
golos.api.getContent(parentAuthorAns,parentPermlinkAns, function(err, result){
tlt=result.title; //определяем титры (назвние) контента
ath=result.author; //определяем автора контента
path=result.parent_author; //определяем автора родителя контента
avotes=result.active_votes;//определяем пользователей проголосовавших за контент
onvot = vt (); //определяем голос робота (голосовал или нет)
//+------------------------------------------------------------------+
//| функция поиска голоса робота за контент |
//+------------------------------------------------------------------+
function vt (){
let str =''; //присваиваем пустое начальное значение возвращаемой переменной
for(let i = 0; i < avotes.length; i++) { //запускаем цикл поиска в массиве пользователей проголосовавших за контент
if( avotes[i].voter == account){ // если находим робота
str='1'; //возвращаемой переменной присваиваем значение '1'
break; //останавливаем цикл
} else str='0';//если робота среди проголосовавших нет, то присваиваем перменной значение '0'
}
return str; //возвращаем значение функции
}
//+------------------------------------------------------------------+
//если титры(название) контента не пустое значение (это пост)
//и автор родительского комментария(поста) является автором контента
//и автор родитель контента пуст
//и нет робота среди проголосовавших за контент
if(tlt!="" && parentAuthorAns==ath && path=="" && onvot=='0'){
golos.broadcast.vote(k, account, parentAuthorAns, parentPermlinkAns, percent, function(err, result) {console.log(err, result); }); //голосуем за пост
post_body="<html><p>Ок, @"+$author+'!</p><p> Я проголосовал за пост: <a href="https://golos.io/@'+ath+"/"+parentPermlinkAns+'">'+tlt+'</a></p></html>'; //формируем тело ответного комментария
golos.broadcast.comment(k,$author,$permlink,account,permlinkcomm,t,post_body,jsonMetadata,function(err, result) {console.log(err, result); }); //размещаем ответный комментарий робота
}
//или если робот голосовал за контент
else if(tlt!="" && parentAuthorAns==ath && path=="" && onvot=='1'){
post_body="<html><p>@"+$author+', я уже голосовал за этот пост.</p></html>'; //формируем тело комментария
golos.broadcast.comment(k,$author,$permlink,account,permlinkcomm,t,post_body,jsonMetadata,function(err, result) {console.log(err, result); }); //размещаем ответный комментарий робота
}
//или если титры (название) контента отсутствуют
//и автор родитель контента не пуст, то это комментарий к комментарию (т.е. комментарий не первого уровня, не к посту)
else if(tlt=="" && path!="" ){
post_body="<html><p>@"+$author+', если необходим мой апвоут за пост, то позовите меня в комментарии первого уровня.</p></html>';//формируем тело ответного комментария
golos.broadcast.comment(k,$author,$permlink,account,permlinkcomm,t,post_body,jsonMetadata,function(err, result) {console.log(err, result); }); //размещаем ответный комментарий робота
}
});
}
}
}
}
// получение данных каждого блока
const SENDBLOCK = currentblock => {
golos.api.getBlock(currentblock, (err, result) => {
if (err) {
console.log(err)
}
else if (result === null){
// если блок не существует активируем триггер
trig.existBlock = false
}
else {
// если блок существует и не было ошибки - отправляем блок в функцию фильтра операций
OPS(result)
.forEach(OPSFILTER)
trig.existBlock = true
}
})
}
// определяем стартовый блок на начало работы скрипта
// каждые 3 секунды увеличивае номер блока на 1
const NEXTBLOCKS = firstblock => {
let currentblock = firstblock
setInterval(() => {
// Увеличиваем только если предыдущий блок был корректно обработан
if(trig.existBlock){
currentblock++
}
SENDBLOCK(currentblock)
}, 3000)
}
// запускаем основные функции через обещания (promises)
dynamicSnap
.then(FIRSTBLOCK)
.then(NEXTBLOCKS)
.catch(e => console.log(e));
Этот же робот легко адаптируется для работы вне Node, непосредственно в браузере в виде вкладки или приложения. А небольшое видоизменение кода позволит его настроить на избранных кураторов (ников) и/или ограничить права на использование по репутации и другим параметрам.
Способы модернизации опубликую чуть позднее.