trovonicknamecolorizer

colorize nicknames in Trovo chat

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         trovonicknamecolorizer
// @namespace    http://tampermonkey.net/
// @version      0.2.7
// @description  colorize nicknames in Trovo chat
// @author       yyko
// @match        https://trovo.live/*
// @icon         https://icons.duckduckgo.com/ip2/trovo.live.ico
// @run-at       document-end
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';

    const maxAttemptsCount=10;
    const attmeptDelay=2000;

    const colorMap=new Map([
      ["red","#FF0000"],//красный
      ["blue","#0000FF"],//синий
      ["green","#008000"],//зелёный
      ["firebrick","#B22222"],//кирпичный
      ["coral","#FF7F50"],//коралловый
      ["yellowgreen","#9ACD32"],//лайм
      ["orangered","#FF4500"],//красно-оранжевый
      ["seagreen","#2E8B57"],//морская волна
      ["goldenrod","#DAA520"],//красное золото
      ["chocolate","#D2691E"],//шоколадный
      ["cadetblue","#5F9EA0"],//серо-голубой
      ["dodgerblue","#1E90FF"],//васильковый
      ["hotpink","#FF69B4"],//ярко-розовый
      ["blueviolet","#8A2BE2"],//индиго
      ["springgreen","#00FF7F"],//салатовый
    ]);
    const colorNames=Array.from(colorMap.keys());

    // Palette icon made by Google (https://www.flaticon.com/authors/google)
    const colorizerSvg='<svg aria-hidden="true" class="svg-icon btn-icon size24" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="24" height="24"><path d="M12 1.5C6.202 1.5 1.5 6.202 1.5 12S6.202 22.5 12 22.5a1.748 1.748 0 0 0 1.295-2.923 1.733 1.733 0 0 1-.437-1.16c0-.969.781-1.75 1.75-1.75h2.059a5.835 5.835 0 0 0 5.833-5.834C22.5 5.677 17.798 1.5 12 1.5zM5.583 12c-.968 0-1.75-.782-1.75-1.75s.782-1.75 1.75-1.75c.969 0 1.75.782 1.75 1.75S6.552 12 5.583 12zm3.5-4.667c-.968 0-1.75-.781-1.75-1.75 0-.968.782-1.75 1.75-1.75.969 0 1.75.782 1.75 1.75 0 .969-.781 1.75-1.75 1.75zm5.834 0c-.969 0-1.75-.781-1.75-1.75 0-.968.781-1.75 1.75-1.75.968 0 1.75.782 1.75 1.75 0 .969-.782 1.75-1.75 1.75zm3.5 4.667c-.969 0-1.75-.782-1.75-1.75s.781-1.75 1.75-1.75c.968 0 1.75.782 1.75 1.75S19.385 12 18.417 12z" style="stroke-width:.0546871"/></svg>';

    const obsConfig={childList:true};
    const classPrefix='clrz_';

    const cpCSSbase='.cp_list,.cp_color{display: inline-block;line-height: 0px;}.cp_list{max-width: 125px;}.cp_color{margin: 0px;width: 25px;height: 25px;transition: all 0.2s ease;}.cp_color_active{box-shadow: 0 0 0 5px white inset;}';

    const onlyChat=!!document.location.href.match('https://trovo.live/chat/.*');

    // local storage tools

    function mapToStr(map){
        return JSON.stringify(Object.fromEntries(map));
    }

    function strToMap(str){
        return new Map(Object.entries(JSON.parse(str)));
    }

    function loadData(entryName='users'){
        let data=localStorage.getItem(entryName);
        if(data){
            return strToMap(data);
        }else{
            return null;
        }
    }

    function saveData(data,entryName='users'){
        localStorage.setItem(entryName,mapToStr(data));
    }

    // --

    // users tools

    let sessionUsers=new Map();
    let users=loadData();
    if(users){
        if(localStorage.getItem('tncts_localUsers')){
            localStorage.removeItem('tncts_localUsers');
        }
    }else{
        users=new Map();
    }

    function addUser(name){
        let userColor=getRandomColorName();
        createUserClass(name,userColor);
        changeUserColor(name,userColor);
        return userColor;
    }

    function delUser(name){
        users.delete(name);
        saveData(users);
    }

    // --

    // color tools

    function getRandomColorName(){
        return colorNames[Math.round(Math.random()*colorNames.length)];
    }

    function getUserColor(name){
        return users.get(name);
    }

    function changeUserColor(name,colorName){
        let ccr=checkColor(colorName);
        if(ccr){
            let cv;
            if(ccr==1){
                cv=colorMap.get(colorName.toLocaleLowerCase());
            }else if(ccr==2){
                cv=colorName;
            }
            changeUserClass(name,cv);
            users.set(name,cv);
            saveData(users);
        }
    }

    function checkColor(colorName){
        if(colorMap.has(colorName)){
            return 1;
        }else if(colorName.match(/^#[0-9a-f]{3,4}$|^#(?:[0-9a-f]{2}){3,4}$/)){
            return 2;
        }else{
            return false;
        }
    }

    function findColorByCode(cc){
        for(let i of colorMap){
            if(i[1]==cc)
                return i[0];
        }
    }

    // --

    // stylesheet

    let ss;

    function initSS(){
        if(ss)
            document.head.appendChild(ss);
        else
            ss=GM_addStyle(makeStyleText());
    }

    function createUserClass(name,color){
        ss.innerHTML=ss.innerHTML+`.${getUserClassName(name)}{color:${color} !important;}`;
        sessionUsers.set(name,color);
    }

    function changeUserClass(name,newcolor){
        let ucn=getUserClassName(name);
        ss.innerHTML=ss.innerHTML.replace(`.${ucn}{color:${getUserColor(name)} !important;}`,`.${ucn}{color:${newcolor} !important;}`);
    }

    function getUserClassName(name){
        return `${classPrefix}${name}`;
    }

    // --

    // pallete

    function makeStyleText(lumshift=0.2){
        let cpCSS=cpCSSbase;
        function cpbCSS(cn,cc){
            function parseCol(a){
                let outp=[];
                a=a.slice(1);
                for(let i=0;i<3;i++){
                    outp.push(parseInt(a.slice(i*a.length/3,(i+1)*a.length/3),16));
                    if(a.length==3)
                        outp[i]*=16;
                }
                return outp;
            }
            function clamp(a,min,max){
                return (a<min)?min:(a>max)?max:a;
            }
            function hex(from){
                let outp='#',tl=true,t;

                for(let i in from)
                    from[i]=Math.round(from[i]);

                for(let i of from)
                    tl=tl&(i%0x11==0);

                for(let i of from){
                    t=i.toString(16);
                    outp+=(tl)?t[0]:(t.length>1)?t:'0'+t;
                }
                return outp;
            }
            function slum(col,s){
                col=col.slice();
                let as=s*3*0xff;
                let sow=0;
                if(s>0){
                    for(let i of col)
                        sow+=1-i/0xff;

                    for(let i=0;i<3;i++)
                        col[i]=clamp(col[i]+(1-col[i]/0xff)/sow*as,0,0xff);
                    return col;
                }else{
                    for(let i of col)
                        sow+=i/0xff;

                    for(let i=0;i<3;i++)
                        col[i]=clamp(col[i]+col[i]/0xff/sow*as,0,0xff);
                    return col;
                }
            }
            function shiftCol(col,s){
                return hex(slum(parseCol(col),s));
            }
            return `.cp_color_${cn}{background-color: ${cc};}.cp_color_${cn}:hover{background-color: ${shiftCol(cc,lumshift)};}`;
        }

        for(let i of colorMap)
            cpCSS+=cpbCSS(i[0],i[1]);

        return cpCSS;
    }

    function createPalleteElement(lstn){
        function n(t){return document.createElement(t);}
        let outp=n('div');
        outp.className='cp_list';

        function onselect(e,me,silent=false){
            me=me||this;
            for(let i of outp.childNodes)
                i.classList.remove('cp_color_active');

            me.classList.toggle('cp_color_active');
            if(!silent&&lstn)
                lstn(outp.nick,me.getAttribute('mycolor'));
        }

        let colblock;
        outp.colblocks=[];
        for(let i of colorMap){
            colblock=n('div');
            colblock.className='cp_color cp_color_'+i[0];
            colblock.setAttribute('mycolor',i[0]);
            colblock.addEventListener('click',onselect);
            outp.appendChild(colblock);
            outp.colblocks.push(colblock);
        }

        outp.style.position='fixed';
        outp.style.zIndex='99999';
        outp.style.display='none';
        outp.setPos=function(x,y){
            outp.style.left=x+'px';
            outp.style.top=y+'px';
        };
        outp.select=function(color){
            for(let i of outp.colblocks){
                if(i.getAttribute('mycolor')==color){
                    onselect(null,i,true);
                    break;
                }
            }
        };

        return outp;
    }

    function onclick(e){
        switch(true){
            case e.target.classList.contains('nick-name'):
                palleteElement.nick=e.target.getAttribute('title');
                palleteElement.select(findColorByCode(getUserColor(palleteElement.nick)));
                //kostyl
                setTimeout(function(){
                    try{
                        let boxpos=document.getElementsByClassName('card-container')[0].getBoundingClientRect();
                        palleteElement.setPos((onlyChat)?boxpos.x+boxpos.width:boxpos.x-palleteElement.getBoundingClientRect().width,boxpos.y);
                    }catch(e){console.log('увы, костыль не сработал',e);}
                },123);
                //--kostyl
            case e.target.classList.contains('cp_color'):
                palleteElement.style.display='block';
            break;
            default:
                palleteElement.style.display='none';
                palleteElement.nick=null;
            break;
        }
        //return false;
    }

    // --

    function onmessage(mutations,observer){
        for(let mutation of mutations){
            for(let msgel of mutation.addedNodes){
                let nameel=msgel.getElementsByClassName('nickname-box')[0];
                let name;
                if(nameel){
                    name=nameel.getElementsByClassName('nick-name')[0].title;
                    if(!users.has(name))
                        addUser(name);
                    else if(!sessionUsers.has(name))
                        createUserClass(name,getUserColor(name));

                    // processing a color change command
                    let msgtextel=msgel.getElementsByClassName('content')[0];
                    if(msgtextel){
                        let msgtext=msgtextel.innerText;
                        let res=msgtext.match(/^!color (.*)/);
                        if(res){
                            let colorValue=res[1];
                            if(checkColor(colorValue)){
                                changeUserColor(name,res[1]);
                            }else{
                                console.warn('color is not available');
                            }
                        }
                    }

                    // applying the color
                    nameel.classList.add(getUserClassName(name));
                }
            }
        }
    }

    // initialization

    let launched;
    let launching;
    let chatElement;
    let chatObserver;
    let palleteElement;

    function findChatElement(){
        return document.getElementsByClassName('chat-list')[0];
    }

    let attemptsLeft=maxAttemptsCount;
    let attemptsTimer;

    function initChat(){
        launched=false;
        launching=true;

        chatElement=findChatElement();
        if(chatElement){
            initSS();

            palleteElement=createPalleteElement(changeUserColor);
            document.body.appendChild(palleteElement);
            window.addEventListener('mouseup',onclick);

            chatObserver=new MutationObserver(onmessage);
            chatObserver.observe(chatElement,obsConfig);

            launched=true;
            launching=false;
            console.warn('started');
        }else{
            if(attemptsLeft>0){
                console.warn('attempts left to start: ',attemptsLeft--);
                attemptsTimer=setTimeout(initChat,attmeptDelay);
            }else{
                console.warn('cant find chat element');
                launching=false;
            }
        }
    }

    function initBaseObs(){
        let baseElement=document.getElementsByClassName('base-container')[0];
        if(baseElement){
            let baseObserver=new MutationObserver(onbasechanged);
            baseObserver.observe(baseElement,obsConfig);
        }else{
            console.warn('cant find base-container');
        }
    }

    function onbasechanged(){
        if(findChatElement()){
            if(!launching&&!launched)
                initChat();
        }else{
            if(launched){
                console.warn('stopping');
                chatObserver.disconnect();
                ss.remove();
                attemptsLeft=maxAttemptsCount;
                launched=launching=false;
            }
        }
    }

    function init(){
        initBaseObs();
        initChat();
    }

    init();

    // --
})();