Jump to content

User:Anpang/Wikipe-tan Chatbox.js

From Wikipedia, the free encyclopedia
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// get css
importStylesheet("User:Anpang01/Wikipe-tan_Chatbox.css");

// add box
const wptcGreeting = "👋 Hello! What would you like me to help with?";

$("body").append(`
    <div class="wptc-box" id="wptc-box">
        <div>
            <img src="/media/wikipedia/commons/a/ac/Wikipe-tan_head.png" class="wptc-image" />
            <span class="wptc-name">Wikipe-tan</span>
            <a href="#" class="wptc-close" id="wptc-close">&times;</a>
        </div>
        <div class="wptc-inner-box">
            <div>
                <div id="wptc-bubbles">
            		<div class="wptc-bubble-left">
                    	<span>${wptcGreeting}</span>
                	</div>
                </div>
                <input type="text" class="wptc-input" id="wptc-input" />
            </div>
        </div>
        <span class="wptc-bottom-links">
            &ensp;
            <a href="https://en.wikipedia.org/wiki/User:Anpang01/Wikipe-tan_Chatbox">JS script</a>
            &ensp;
            <a href="https://en.wikipedia.org/wiki/Wikipedia:Wikipe-tan">Based on Wikipe-tan</a>
            &ensp;
        </span>
    </div>
`);

// close box handler
$("#wptc-close").on("click", function() {
	$("#wptc-box").hide();
});

// conversation data
const wptcConvers = [
    [
        "Hello!",
        "Hello {username}! What would you like me to help with?",
        "Nothing, actually.",
        "Okay, I'll just stay here in case you need my help."
    ],
    [
    	"What skin does Wikipedia use?",
    	"Wikipedia uses the Vector skin, and this page uses the {skin} skin."
    ],
    [
    	"What skin am I using?",
    	"You are using the {skin} skin."
    ],
    [
    	"What is the name of this website?",
    	"This is the English {sitename}."
    ]
];
const wptcDontKnow = [
    "Sorry, I couldn't understand you.",
    "Sorry, what do you mean by that?",
    "Sorry {username}, I couldn't understand that."
];
const wptcVariables = {
    "username": mw.config.get("wgUserName"),
    "skin": `${mw.config.get("skin")[0].toUpperCase()}${mw.config.get("skin").slice(1)}`.replace("-", " "),
    "sitename": mw.config.get("wgSiteName")
}

// helper functions
function wptcRandomElement(array) {
    return array[Math.floor(Math.random() * array.length)];
}

// from https://github.com/saniales/gestalt-pattern-matcher/blob/master/index.ts
function wptcGestaltSimilarity(first, second) {
    let stack = [first, second];
    let score = 0;
    
    while(stack.length != 0) {
        const first_sub_string = stack.pop();
        const second_sub_string = stack.pop();
        
        let longest_sequence_length = 0;
        let longest_sequence_index_1 = -1;
        let longest_sequence_index_2 = -1;

        for(let i = 0; i < first_sub_string.length; i++) {
            for(let j = 0; j < second_sub_string.length; j++) {
                let k = 0;
                while(i+k < first_sub_string.length && j+k < second_sub_string.length && first_sub_string.charAt(i+k) === second_sub_string.charAt(j+k)) {
                    k++;
                }
                if(k > longest_sequence_length) {
                    longest_sequence_length = k;
                    longest_sequence_index_1 = i;
                    longest_sequence_index_2 = j;
                }
            }
        }
    
        if(longest_sequence_length > 0) {
            score += longest_sequence_length * 2;
            if(longest_sequence_index_1 !== 0 && longest_sequence_index_2 !== 0) {
                stack.push(first_sub_string.substring(0, longest_sequence_index_1));
                stack.push(second_sub_string.substring(0, longest_sequence_index_2));
            }
            if(longest_sequence_index_1 + longest_sequence_length !== first_sub_string.length && 
                longest_sequence_index_2 + longest_sequence_length !== second_sub_string.length) {
                stack.push(first_sub_string.substring(longest_sequence_index_1 + longest_sequence_length, first_sub_string.length));
                stack.push(second_sub_string.substring(longest_sequence_index_2 + longest_sequence_length, second_sub_string.length));
            }
        }
    }
    
    return score / (first.length + second.length);
}

// get response
function wptcGetResponse(input) {
    contenders = [];
    highestScore = 0;

    for(const conver of wptcConvers) {
        for(const [index, message] of conver.entries()) {
            score = wptcGestaltSimilarity(message, input);

            if(conver.length == (index + 1)) {
                continue;
            }
            nextMessage = conver[index + 1];
            
            if(score > highestScore) {
                contenders = [nextMessage];
                highestScore = score;
            } else if(score == highestScore) {
                contenders.push(nextMessage);
            }
        }
    }

    let response = wptcRandomElement(highestScore < 0.3? wptcDontKnow: contenders);
    for(const [name, value] of Object.entries(wptcVariables)) {
        response = response.replace(`{${name}}`, value);
    }
    return response;
}

// submit handler
$("#wptc-input").keypress(function(key) {
    if(key.which === 13) { // is enter key
        // get and clear input
        let input = $("#wptc-input").val();
        $("#wptc-input").val("");
        
        // do stuff
        $("#wptc-bubbles").append(`
            <div class="wptc-bubble-right">
                <span>${input}</span>
            </div>
            <div class="wptc-bubble-left">
                <span>${wptcGetResponse(input)}</span>
            </div>
        `);
    }
});