Dark Voice

Темный скин golos.io с монитором активности пользователей из blockchain

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name Dark Voice 
// @namespace golos.io 
// @description Темный скин golos.io с монитором активности пользователей из blockchain
// @description:ru Темный скин golos.io с монитором активности пользователей из blockchain
// @grant none
// @version 0.23
// @include https://golos.io/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js
// ==/UserScript==
// 
var myname = 'vik'; // Поменяйте на свое имя и переместите сокеты выше - будет работать быстрей :)
var socket = new WebSocket('wss://ws.golos.io'),
    account = myname;
startblock = 0;
socket.onopen = function(event) {
    socket.send(JSON.stringify({
        id: 1,
        method: 'get_dynamic_global_properties',
        'params': []
    }));
};
socket.onmessage = function(event) {
    data = JSON.parse(event.data);
    startblock = data.result.last_irreversible_block_num;
};


// Прописываем CSS и HTML
$('.Header__top-steemit').html('<a href="/@vik/">Dark Voice<span class="beta">@vik</span></a>');
var inlinecss = "<style>.Voting__button path,.Voting__button-down a:hover path{fill:#fff}#opclose,.svote a{vertical-align:middle}.Header,body{background-color:#36465d;color:#fff}.VerticalMenu>li>a,.menu a{color:#fff}.VerticalMenu>li>a:hover{color:#101823;line-height:1.5rem}.Header__sub-nav .active a,.Header__sub-nav li:hover a,.PostSummary__header>h2>a,.PostsIndex__topics,.PostsIndex__topics small a,.Topics__title{color:#fff}.Header__sub-nav li>a{color:#fff;border:0;line-height:1.2em;font-size:.9em;background-color:#101823}.Header__sub-nav li{padding:.5rem 0;position:relative;overflow:hidden}.menu .buttons{display:none}.Header__sub-nav li>a:after{content:'';width:0;height:2px;background:#fff;z-index:1;position:absolute;bottom:0;left:0;transition:.6s all ease}.Header__sub-nav li:hover>a:after{content:'';width:100%}.Header .menu,.Header__sub-nav,.Header__top{background-color:#101823;border-bottom:1px solid #0b1119}#content{background:#36465d}.PostsIndex__topics{border-radius:3px;padding:5px;transition:.4s all ease;height:35px;overflow:hidden;position:fixed;top:55px;border:0;right:-160px;background:#184e86;box-shadow:0 0 35px -10px #000;z-index:700}ul.Topics li a{background:#fff;padding:0 3px;border-radius:3px;margin:3px}.PostsIndex__topics:hover{box-shadow:0 0 75px -10px #000;height:450px;overflow:auto;right:-30px}img.PostSummary__image{width:100%;max-width:640px;height:auto;border:0;padding:0;float:none;display:block}.PostSummary{position:relative;margin:0 2% 50px 0;border-radius:3px;box-shadow:0 15px 50px -15px #000;max-width:580px;background:#101823}.PostSummary.with-image .PostSummary__content,.PostSummary.with-image .PostSummary__reblogged_by,.PostSummary__content{margin:.5rem}.PostSummary__collapse{position:absolute;right:5px;top:5px}.PostSummary__body.entry-content{white-space:normal;color:#a6acb5;margin:0 0 30px}.PostSummary__footer{color:#fff}.Voting__inner{border:0}.Voting__button circle{stroke:#fff}.Voting__button-up>a:hover circle,a.confirm_weight:hover circle{fill:#4ba2f2;stroke:#4ba2f2}.Voting__button-up .Icon{box-shadow:0 0 15px rgba(0,0,0,.7);border-radius:100%;transform:scale3d(1,1,1) rotate(0);transition:all 1s cubic-bezier(.4,0,0,1.67)}.PostSummary:hover span.usersg{position:relative;transition:1s all ease}.PostSummary:hover .Voting__button-up .Icon{box-shadow:0 0 25px rgba(0,0,0,.7);transform:scale3d(2.2,2.1,2.6) rotate(720deg)}.dropdown-pane{background:#101823;border:0;box-shadow:0 0 20px #000;transition:1s all ease;color:#fff}.vcard>strong a,span.Author{font-weight:200;text-shadow:none;border-radius:10px;color:#fff}#closeop,.ReplyEditor{box-shadow:0 0 30px -10px #000}.Voting__adjust_weight .weight-display,.Voting__adjust_weight_down .weight-display{margin-left:20px;color:#fff;font-size:1.5rem}.Voting__adjust_weight,.Voting__adjust_weight_down{padding:10px 5px;margin-right:0;width:400px;overflow:hidden}.vcard>strong a{background:#4ba2f2;padding:0 7px 1px}span.Author{position:relative;background:#484848;padding:1px 6px 2px 12px}.Reputation{font-size:.8rem;border:0;border-radius:5px;background:#ffae00;color:#fff;padding:0 6px 0 3px}.vcard>strong a:before{content:'#';font-size:.6rem;margin-right:3px;color:#fff}.PostSummary__time_author_category{font-size:.7rem;border:0}.VotesAndComments{float:right}.PostsIndex.row{max-width:100%}.Voting__inner{margin-right:0}.grid-item{float:left;transition:.4s all ease}.grid-item,.grid-sizer{width:22%}.gutter-sizer{width:4%}#showanswer{position:fixed;right:26px;top:118px;background:#063465;color:#fff;font-size:14px;border-radius:3px;padding:1px 15px;z-index:400}span.usersg{transition:all .4s ease;background:#009c64;padding:1px 5px 2px}#drkloader{width:auto;display:block;height:100%;overflow:auto;overflow-x:hidden}#drkwidget,.answerholder{position:fixed;width:50%}.ReplyEditor{background:#fff;padding:20px 0;border-radius:5px;color:#000}ul#drkstream{margin:0;padding:0 15px;background:rgba(1,14,31,.9);height:90%;overflow-x:auto;overflow-y:auto}#drkstream li{display:none;list-style-type:none;font-size:.8rem}.svote a{background:#5974ff;color:#fff;padding:1px 5px;border-radius:10px;line-height:0;overflow:hidden;font-size:11px;font-weight:700}.scomment i{background:green;font-style:normal;padding:0 4px;border-radius:15px}.Voting span{color:#1dd492;text-shadow:0 0 3px #19943e}.drklog p{line-height:1;margin:0;background:#184e86;padding:10px}.insertsila strong{font-size:20px;background:url(https://golos.io/images/golospower-badge.jpg) no-repeat #fff;background-size:contain;padding:0 15px 0 35px;color:#1b5d9d;border-radius:30px}.drklog{text-align:center}#drkwidget{z-index:100;display:none;background:url(https://s28.postimg.org/bvu4yda25/endless_3d_cube_gif.gif) no-repeat #184e86;background-size:cover;top:110px;right:15px;height:600px;height:calc(100vh - 180px);border-radius:3px;box-shadow:0 0 10px -1px #000;overflow:hidden}ul#drkstream li b{background:rgba(75,162,242,.14);padding:1px 8px;border-radius:10px;font-weight:200}ul#drkstream li strong{background:rgba(114,255,98,.51);padding:1px 8px;border-radius:10px}.scomment div,.svote div{font-size:.8rem;height:.8rem;overflow:hidden;vertical-align:middle;line-height:.8rem;max-width:200px;display:inline-block;color:#3b4a5a}#closeop,#drkmenu,#drkstory,.answertarget .Voting{display:none}#drkmenu{text-align:center}#post_overlay{color:#000}.answerholder{z-index:500;background:#101823;display:none;top:150px;padding:15px;right:14px}#closeop,#opclose{background:#063465;border-radius:3px;font-size:14px}.answertarget{overflow:auto;height:500px;width:740px}#opclose{position:absolute;left:20px;top:10px;padding:3px 15px 5px;line-height:14px}#closeop{z-index:400;position:fixed;top:150px;right:26px;padding:5px 10px}.beforeload{transition:1s all}#drkstream::-webkit-scrollbar,body::-webkit-scrollbar{width:7px;height:9px}#drkstream::-webkit-scrollbar-track,body::-webkit-scrollbar-track{background:#053363}#drkstream::-webkit-scrollbar-thumb,body::-webkit-scrollbar-thumb{background:rgba(255,255,255,.4)}#drkstory{overflow:auto;height:100%;opacity:.98}#drkstory tbody,.UserWallet tbody{border:0!important;background:#101823}#drkstory table tbody tr:nth-child(even),.UserWallet table tbody tr:nth-child(even){background-color:#063465}.UserWallet__balance.row{background:#fff;padding:15px;color:#000;font-weight:700}.reveal.fade.in{background:#20242a;color:#fff;border:0;box-shadow:0 0 50px #000}a.go-top{position:fixed;background:#334258;bottom:10px;left:10px;color:#fff;padding:10px;box-shadow:0 0 20px #000;border-radius:3px}.answertarget .PostSummary{position:relative;margin:10px 0;border-radius:3px;box-shadow:0 15px 50px -15px #000;max-width:660px;background:#101823;color:#fff}.answertarget .PostSummary__body.entry-content{color:#fff;margin:0}.Comment .highlighted { background: #415879; padding: 5px; border-radius: 10px; }.Post a { color: #9cd0ff; }</style>";
var somehtml = '<a href="#0" class="go-top">Вверх</a><div id="drkwidget"><div class="drklog"><p id="silag"><span class="insertsila"><strong>Сила Голоса...</strong></span></p><span id="opclose">Закрыть</span><span id="currblock">Загружаем текущий блок...</span><span id="dwitness"></span></div><div id="drkmenu">Моя история</div><div id="drkstory"></div><ul id="drkstream"><li class="beforeload"><center>Подключаемся к ноде... Для корректной работы используйте только одну вкладку Голоса</center></li><li class="beforeload2"><center>В этой ноде идет трансляция действий пользователей: комментарии, голосование, упоминания, фолловинг и другое. При низком качестве подключения возможны потери событий из трансляции. Для минимизации потерь не открывайте несколько вкладок Голоса одновременно!</center></li></ul></div><div id="closeop">Мониторинг</div>';
$(inlinecss).appendTo("head"); // Устанавливаем CSS стили
$(somehtml).appendTo("body"); // Вставляем необходимые html элементы

// Меняем в постах маленькие фото на большие: 256x128 > 640x480 
function imgtoggle() {
    var tinyimg = new RegExp('/256x128/'), // Берем ссылки на маленькие превью
        bigimg = new RegExp('640x480'); // Меняем в ссылка значение разрешения фотографии, обращаясь к проксификатору по ссылке https://imgp.golos.io/640x480/
    $('img.PostSummary__image').each(function() {
        var image = $(this);
        image.attr('src', image.attr('src').replace(tinyimg, bigimg)); // Тут происходит ранее описанная замена 256x128 на 640x480
    });
}
imgtoggle();
setTimeout(function() {
    $('#drkwidget').slideDown();
}, 2000);
$('#opclose').on('click', function() {
    $('#drkwidget').slideUp();   $('.PostsIndex.row' ).css('max-width',' 80%');
    $('#closeop').slideDown();
});
$('#closeop').on('click', function() {
    $('#drkwidget').slideDown();
    $(this).slideUp(); $('.PostsIndex.row' ).css('max-width',' 100%');
});




function drkwait() {
    socket = new WebSocket('wss://ws.golos.io'); account = myname;
    var movementTotal, silaTotal;
    socket.onopen = function(event) {
        socket.send(JSON.stringify({
            id: 1,
            method: 'get_dynamic_global_properties',
            'params': []
        }));
    };
   socket.onmessage = function(event) {
        var silaGolosa, powerMovement, data = JSON.parse(event.data),
            stream = document.getElementById('drkstream');
        lastblock = startblock;
        merkle = data.result.transaction_merkle_root;
        if (merkle !== 0 && startblock > 100) {
            if (data.id === 1) {
                movementTotal = data.result.total_vesting_shares.split(' ')[0];
                silaTotal = data.result.total_vesting_fund_steem.split(' ')[0];

                socket.send(JSON.stringify({
                    id: 2,
                    method: 'get_accounts',
                    params: [
                        [account]
                    ]
                }));
                socket.send(JSON.stringify({
                    id: 3,
                    method: 'get_block',
                    'params': [lastblock]
                }));
                //  socket.send(JSON.stringify({ id: 4, method:'get_account_history', params: [ [account] ] })); История аккаунта
                document.getElementById('currblock').innerHTML = '<span class="insertblock"><strong>' + lastblock + '</strong> Последний блок. </span>';
            }
            if (data.id === 2) {
                powerMovement = data.result[0].vesting_shares.split(' ')[0];
                silaGolosa = silaTotal * (powerMovement / movementTotal);
                document.getElementById('silag').innerHTML = '<span class="insertsila"><strong>' + silaGolosa + '</strong></span>';
            }

            if (data.id === 3) {
                tx = data.result.transactions[0].operations[0][1];
                wtnss = data.result.witness;
                drkvoter = JSON.stringify(tx.voter);
                drkpower = JSON.stringify(tx.weight);
                drkauthor = JSON.stringify(tx.author);
                drklink = JSON.stringify(tx.permlink);
                drkcomm = JSON.stringify(tx.parent_author);
                drkbodycomm = JSON.stringify(tx.body);
                drknewbie = JSON.stringify(tx.new_account_name);
                drkfl = tx.json; // Фолловинг (подписки)
                mention = JSON.stringify(tx.to); // Mention упоминания
                mentmemo = JSON.stringify(tx.memo);
                document.getElementById('dwitness').innerHTML = '<span class="inserwtnss"> В ноде <strong>' + wtnss + '</strong></span>';
                if (typeof mentmemo !== "undefined") {
                    memocut = mentmemo.slice(26);
                    stream.insertAdjacentHTML('afterbegin', '<li class="sfollow"><b>' + mention + '</b> упомянул ' + memocut + '</li>');
                }
                if (typeof drknewbie !== "undefined") {

                    stream.insertAdjacentHTML('afterbegin', '<li class="snewbie">Новый пользователь > <strong>' + drknewbie + '</strong>!</li>');
                }
                if (typeof drkcomm !== "undefined" && typeof drkauthor !== "undefined") {
                    stream.insertAdjacentHTML('afterbegin', '<li class="scomment"><b>' + drkauthor + '</b> комментирует пост <strong>' + drkcomm + '</strong> <div>' + drklink + '</div></li>');
                }
                if (typeof drkvoter !== "undefined" && typeof drkauthor !== "undefined") {
                    stream.insertAdjacentHTML('afterbegin', '<li id=' + drkauthor + ' class="svote"><b>' + drkvoter + '</b>  <a title=' + drklink + ' href=' + drklink + '>></a>  голосует с силой ' + drkpower + ' за <strong>' + drkauthor + '</strong>    <div>' + drklink + '</div></li>');
                }
                if (typeof drkfl !== "undefined") {
                    df = JSON.parse(drkfl);
                    dfollower = df[1].follower;
                    dfollowin = df[1].following;
                    stream.insertAdjacentHTML('afterbegin', '<li class="sfollow"><b>' + dfollower + '</b> подписка на <strong>' + dfollowin + '</strong></li>');

                }
            }

        }



    };
}




// Получаем параметры, который пользователь может добавить в конец своей ссылки на аватар.  Синтаксис //domain.tld/img.jpg?css=[css стили] 
// Так же получаем юзернэйм для которого нужно показывать блок ответов
function waitfor() { // Внутри функции будем ждать полной загрузки страницы и проверять когда появятся необходимые нам ссылки

    var Upic = $('.Userpic');
    var userpicUrl = $('.Userpic img').attr('src'); // Проверяем ссылку на аватар
    var usernameUrl = Upic.parent().attr('href'); // Проеверяем ссылку на юзернэйм. Нам нужно получить из нее ник текущего пользователя, что бы ответы в виджет загружались именно для вас. 
    // Если ссылки уже доступны, выдираем только нужные нам параметры:  
    // Фильтруем и получаем параметры, который пользователь добавил в своем аккаунте в конец ссылки на аватар  например //вк.ком/рукалицо.jpg?css=[#content{background:black;}] сделает фон контента черным

    function getParameterByName(name, url) {
        if (!url) {
            url = userpicUrl;
        }
        name = name.replace(/[\[\]]/g, "\\$&");
        var regex = new RegExp("[?&]" + name + "(=([^&]*)|&||$)"),
            results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, " "));
    }
    // Функция с помощью который мы загрузим ответы в будущем.
    function answerupdate() {
        $('.answertarget').load(usernameUrl + '/recent-replies/ #posts_list');
    }
if (Upic.length) {
  
  myname = usernameUrl.slice(2);
 var isVisible = function(obj) {
            return obj.offsetWidth > 0 && obj.offsetHeight > 0;
        };
        var curry = function(uncurried) {
            var parameters = Array.prototype.slice.call(arguments, 1);
            return function() {
                return uncurried.apply(this, parameters.concat(Array.prototype.slice.call(arguments, 0)));
            };
        };
        var getAndAddToCallbacks = function(socket, callbacks, method, params, callback) {
            callbacks.push(callback);
            var data = {
                id: callbacks.length - 1,
                method: method,
                params: params
            };
            socket.send(JSON.stringify(data));
        };
        var runFun = function(funs, id, data) {
            funs[id](data);
        };
        var socket = new WebSocket('wss://ws.golos.io'),
            callbacks = [];
        var getResult = curry(getAndAddToCallbacks, socket, callbacks),
            runCallback = curry(runFun, callbacks);
        socket.onopen = function(event) {
            getResult('get_dynamic_global_properties', [], function(data) {
                var totalVestingShares = data.result.total_vesting_shares.split(' ')[0],
                    totalVestingFundSteem = data.result.total_vesting_fund_steem.split(' ')[0];
                var nodes = [].slice.call(document.querySelectorAll('[itemprop=author] strong')).reduce(function(nodes, node) {
                    if (!isVisible(node) || node.ssp__hasSp) {
                        return nodes;
                    }
                    nodes.push(node);
                    return nodes;
                }, []);
                var accounts = nodes.map(function(node) {
                    return node.innerHTML;
                });
                getResult('get_accounts', [accounts], function(data) {
                    data.result.forEach(function(account, i) {
                        var vestingShares = account.vesting_shares.split(' ')[0];
                        var steemPower = totalVestingFundSteem * (vestingShares / totalVestingShares);

                        var parent = nodes[i].parentNode;

                        console.log(account.name + ' ' + steemPower);
                        nodes[i].outerHTML = '' + account.name + ' <span class="usersg">' + Math.round(steemPower) + '</span>';
                        nodes[i].ssp__hasSp = true;


                    });
                    
                });
            });




        };
       socket.onmessage = function(event) {
            var data = JSON.parse(event.data);
            runCallback(data.id, data);
        };
       setInterval(function() {
            startblock++;
            drkwait();

            imgtoggle();
            $('#drkstream li,#drkmenu').slideDown();


        }, 5000); // Проверяем новые блоки. 



        $('.Header__top-steemit').html('<a href="/@vik/">Dark Voice<span class="beta">@vik</span></a>');
        $('.beforeload').css('opacity', '0');

       
      
        




        // Даем возможность обновлять ответы нажатием кнопки "Обновить ответы"
        $('<div class="answerholder"><p id="answerupdate">Обновить ответы</p><div class="answertarget">Загрузка...</div></div><div id="showanswer">Мои ответы</div>').appendTo("body");

        $('#answerupdate').on('click', function() {
            answerupdate();
        });

        // Даем возможность открывать/закрывать блок ответов
        var isActive = true;
        $('#showanswer').on('click', function() {
            if (isActive) {
                answerupdate();


                $('.answerholder').slideDown();
            } else {
                $('.answerholder').slideUp();
            }
            isActive = !isActive;

        });


        // моя история

        $('#answerupdate').on('click', function() {
            answerupdate();
        });

        // Даем возможность открывать/закрывать блок ответов
      
        $('#drkmenu').on('click', function() {
            if (isActive) {
                $('#drkstory').load(usernameUrl + '/transfers/ table'); // загрузка истории
                $('#drkstory').slideDown();
            } else {
                $('#drkstory').slideUp();
            }
            isActive = !isActive;

        });



        // Получаем то, что после ?css= и перед &  


        // Убираем квадратные скобки по бокам из переменной
        var cleaning = getParameterByName('css');

        if (cleaning !== null) {
            var stringcss = cleaning.substring(1, cleaning.length - 1);
            // Оборачиваем в теги стиля    

            // Если параметр существует 
            var cleanCSS = $('<style></style>').append(stringcss);
            // Подключаем наш кастом стиль. На данный момент он применяется только после полной загрузки страницы, но в следующей версии скрипта я это исправлю.
            cleanCSS.appendTo("head");
        }
        //C остальными пармаетрами проще. Можно получить параметр после каждого & в формате //вк.ком/рукалицо.jpg?option1=ON&option2=OFF&option3=BIG
        // Пока опции есть как пример и демо функционала, осталось придумать какие опции могуть быть нужны. Например фиксировать ли кнопки редактора или нет. Или отключить большинсво ненужных элементов. 
        // В качестве примера отключим топики - метки в сайдбаре 
        var notopics = getParameterByName('topics');
        if (notopics == 'OFF') {
            $('.PostsIndex__topics').slideUp(); // Топики улетают
            // Добавляем кнопку, по нажатию которой топики выпадут вниз
            $('<div id="slidetopics">Топики</div>').appendTo("body");

            $('#slidetopics').on('click', function() {
                if (isActive) {

                    $('.PostsIndex__topics').slideDown();
                } else {
                    $('.PostsIndex__topics').slideUp();
                }
                isActive = !isActive;
            });

        }
        // Для отключения топиков вставьте в ссылку аватара параметр &notopics=OFF 
        // В следующей версии скина будет больше параметров и настроек  


    }
    // Так как мы все еще внутри условия "Если аватарка уже загрузилась" , то если она не загрузилась - мы будем рекурсивно проверять и ждать ее каждую секунду   
    else {
        setTimeout(waitfor, 1000);
    }
}
waitfor();

// Добавляем кнопку-стрелку возвращения в начало страницы
$(document).ready(function() {
    // Show or hide the sticky footer button
    $(window).scroll(function() {
        if ($(this).scrollTop() > 200) {
            $('.go-top').fadeIn(200);

        } else {
            $('.go-top').fadeOut(200);
        }
    });

    // Animate the scroll to top
    $('.go-top').click(function(event) {
        event.preventDefault();


        $('html, body').animate({
            scrollTop: 0
        }, 300);
    });
});


// Сообщение в консоли браузера.
console.log('Вы установили userscript от golos.io/@vik и сейчас вы смотрите консоль :) ВНИМАНИЕ! Перед установкой этого или похожего скрипта убедитесь, что вы нашли его в надежном источнике! Если вы не уверены, что скрипт оригинальный - обратитесь к опытным пользователям и попросите проверить его на предмет внедрения вредного кода. Вы можете обратиться ко мне в голосе golos.io/@vik/ ');