Greasy Fork is available in English.

Chatbot

botとbotマネージャーはこれを@requireで読み込ませる必要があります。

Dette scriptet burde ikke installeres direkte. Det er et bibliotek for andre script å inkludere med det nye metadirektivet // @require https://greasyfork.org/scripts/387372-chatbot/code/Chatbot.js?version=726700

/*
botとbotマネージャーはこれを@requireで読み込ませる必要があります。
またbotマネージャーはこれを読み込む前にyaju1919_libraryを読み込ませる必要があります。
*/
const win = window.unsafeWindow||window;
if(!win.Chatbot) win.Chatbot = {};
/*
①第一引数
update: 自分以外の新しい発言があれば
{
    name: "発言者の名前の文字列", (省略可能)
    text: "発言内容の文字列", (必須)
    myName: "自分の名前", (省略可能)
}
の連想配列を返し、なければnullを返す関数
post: 引数の文字列をチャットに投稿する関数
●省略可能
greet: 新しい入退室情報があれば挨拶の文字列を返し、なければnullを返す関数
auto: 何も更新がない場合、定期的に発言する文字列を返す関数

font: 文字の色(cssのcolor値)
back: 背景の色(cssのbackground-color値)
*/
const appendChatbotManager = ({update,post,greet,auto,font="white",back="rgba( 0, 0, 0, 0.7 )"}) => {
    if(!update || !post) return console.error("必須な引数のどれかが欠けています");
    const yaju1919 = yaju1919_library;
    const getType = yaju1919.getType;
    const talked_log = []; /* 会話ログの配列、一番最後が最新の投稿
    { name: "発言者の名前の文字列", text: "発言内容の文字列" }
    nameには、名前が無い場合は自動で"名無し"、botの投稿の場合はnullが入っている
    */
    const addTalked_log = (name,text) => talked_log.push({
        name: name,
        text: text
    });
    const makeSpace = (()=>{
        let i = 0;
        return () => " ".repeat((++i)%5);
    })();
    let latest_said_time; // 最後に発言した時間
    const saying_stack = []; // 発言予約リスト
    const say = str => { // 発言処理
        if(!saying_stack.length) {
            rentou_flag = false;
            setWait(false);
        }
        if(!str.trim().length) return;
        post(str+makeSpace());
        latest_said_time = new Date().getTime();
        addTalked_log(null,str);
    };
    let rentou_flag, first_time;
    const interval_saying = () => {
        if(!saying_stack.length) return;
        const now = new Date().getTime();
        if(rentou_flag){ // 連投モード
            if(now - latest_said_time < RandBetween.rento.val*1000) return;
            say(saying_stack.shift());
            RandBetween.rento.set();
        }
        else {
            if(now - first_time < RandBetween.rug.val*1000) return;
            rentou_flag = true;
            say(saying_stack.shift());
            RandBetween.rug.set();
        }
    };
    const addStack = arg => {
        switch(getType(arg)){
            case "Number":
            case "String":
                setWait(true);
                first_time = new Date().getTime();
                saying_stack.push(arg);
                break;
            case "Array":
                setWait(true);
                first_time = new Date().getTime();
                for(const v of arg) {
                    if(getType(v)==="String") saying_stack.push(v);
                }
                break;
            default:
                setWait(false);
                break;
        }
    };
    let wait_flag; // 次の反応まで待機するフラグ
    const setWait = flag => {
        wait_flag = flag;
        system_message(flag ? "wait_flagがtrueになりました。" : null);
    };
    const bot_main = () => {
        if(!run_flag() || wait_flag) return;
        const obj = update();
        if(yaju1919.getType(obj) !== "Object") return;
        for(const k of ["name","text","myName"]){
            if(yaju1919.getType(obj[k]) !== "String") obj[k] = "";
            obj[k] = obj[k].trim();
            if(!obj[k].length){
                if(k==="name") obj[k] = "名無し";
                else if(k==="text") return;
                else if(k==="myName") obj[k] = "私";
            }
        }
        addTalked_log(obj.name, obj.text);
        if(Math.random()*100 > input_rate()) return; // 確率で無視
        obj.log = talked_log;
        obj.wait = () => {
            setWait(true);
            return str => addStack(str);
        };
        addStack(talk(obj));
    };
    const greet_main = () => {
        if(!greet_flag() || wait_flag) return;
        addStack(greet());
    };
    const auto_main = () => {
        if(!auto_flag() || wait_flag) return;
        const now = new Date().getTime() - latest_said_time;
        if(now > RandBetween.auto.val*1000) {
            addStack(auto());
            RandBetween.auto.set();
        }
    };
    const interval = () => {
        interval_saying(); // 発言処理
        greet_main(); // 自動挨拶
        bot_main(); // 自動会話
        auto_main(); // 定期投稿
    };
    //------holder要素-----------------------------------------------------------------------------------------------------------------------
    let toggle_flag;
    const toggle = () => {
        toggle_flag = !toggle_flag;
        parent_holder.css({
            right: toggle_flag ? 0 : -parent_holder.width()*1.4,
        });
    };
    const toggle_btn = $("<button>",{text:"bot"}).appendTo($("body"))
    .css({
        backgroundColor:"red",
        color:"yellow",
        position: "fixed",
        zIndex: 364364,
        right: 0,
        top: 0,
    }).click(toggle);
    const parent_holder = $("<div>",{text:"botマネージャー"}).appendTo($("body")).css({
        padding: "1em",
        color: font,
        backgroundColor: back,
        position: "fixed",
        zIndex: 114514,
        top: 0,
        height: "100vh",
        width: "22em",
        maxWidth: "90vw",
        transition: "all .5s",
        overflow: "scroll",
        "text-align": "left",
        fontSize: "1em"
    });
    parent_holder.css("right",-parent_holder.width()*1.2);
    const main_holder = $("<div>").appendTo(parent_holder); // bot選択、bot起動ボタンなど
    const test_holder = $("<div>").appendTo(parent_holder); // テスト
    const sub_holder = $("<div>").appendTo(parent_holder); // 絶対発言するマンの優先順位、botごとのオプション機能
    parent_holder.children().each((i,e)=>$(e).after("<br>"));
    //-----------------------------------------------------------------------------------------------------------------------------
    const rand = yaju1919.rand;
    const botList = {}; // この中に全てのBotのmain関数が入っている
    const bot_appendElementFunc_List = {};
    const updateSelect = () => {
        const keys = Object.keys(botList);
        for(const k in win.Chatbot){
            if(keys.indexOf(k)!==-1) continue;
            const now = win.Chatbot[k];
            PriorityList[k] = {};
            PriorityList[k].v = 1;
            switch(getType(now)){
                case "Function":
                    botList[k] = now;
                    appendOption(k);
                    break;
                case "Object":
                    if(getType(now.main) === "Function") botList[k] = now.main;
                    if(getType(now.config) === "Function") bot_appendElementFunc_List[k] = now.config;
                    if(!isNaN(now.priority)) PriorityList[k].v = now.priority;
                    appendOption(k);
                    break;
            }
        }
    };
    const select = $("<select>").appendTo(main_holder).click(()=>updateSelect()).change(()=>setSub_holder(select.val()))
    .css({
        maxWidth: "90%",
        minWidth: "90%"
    });
    const appendOption = title => $("<option>",{text:title}).val(title).appendTo(select);
    appendOption("▼botの種類を選択");
    appendOption("★ランダム");
    appendOption("★絶対発言するマン");
    const talk = obj => {
        if(wait_flag) return;
        let v = select.val();
        const keys = Object.keys(botList);
        if(v==="★ランダム") return botList[rand(keys)](obj);
        else if(v==="★絶対発言するマン") {
            let ar = keys.filter(v=>PriorityList[v].f()>0);
            while(ar.length){
                if(wait_flag) return;
                const max = Math.max.apply(null,ar.map(v=>PriorityList[v].f())); // 最大値を求める
                let ar2 = ar.filter(v=>PriorityList[v].f()===max);
                while(ar2.length){
                    if(wait_flag) return;
                    const rnd = rand(ar2);
                    const result = botList[rnd](obj);
                    for(const k in PriorityList) PriorityList[k].e.css({backgroundColor:"rgba( 0, 0, 0, 0 )",color:font});
                    PriorityList[rnd].e.css({backgroundColor:"yellow",color:"red"});
                    if(result) return result;
                    ar2 = ar2.filter(v=>v!==rnd);
                }
                ar = ar.filter(v=>PriorityList[v].f()<max);
            }
            return;
        }
        if(botList[v]) return botList[v](obj);
    };
    //------------------------------------------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------------------------------------------
    const resetSaying = () => { // 発言処理を初期化
        setWait(false);
        rentou_flag = false;
        latest_said_time = new Date().getTime();
        while(saying_stack.length) saying_stack.pop();
        RandBetween.auto.set(); // 定期投稿の数値を再設定
        for(const k in RandBetween) RandBetween[k].set();
    };
    const run_flag = yaju1919.appendCheckButton(main_holder,{
        title: "bot起動",
        change: resetSaying
    });
    const input_rate = yaju1919.appendInputNumber(main_holder,{
        title: "反応確率",
        value: 100,
        placeholder: "[%]",
        min: 0,
        max: 100,
        save: "kakuritsu"
    });
    const RandBetween = {};
    const RandBetweenKeys = ["rug","rento"];
    if(auto) RandBetweenKeys.push("auto");
    RandBetweenKeys.forEach(k=>{
        RandBetween[k] = {};
        RandBetween[k].val;
        RandBetween[k].set = () => {
            RandBetween[k].val = RandBetween[k].input();
        };
    });
    RandBetween.rug.input = yaju1919.appendRandBetween(main_holder,{
        title: "遅延時間",
        change: RandBetween.rug.set,
        value: [3,9],
        save: "chien2",
        placeholder: "[秒]",
        min: 0,
        max: 60,
    });
    RandBetween.rento.input = yaju1919.appendRandBetween(main_holder,{
        title: "連投間隔",
        change: RandBetween.rento.set,
        value: [3,9],
        save: "rentou",
        placeholder: "[秒]",
        min: 0,
        max: 60,
    });
    const greet_flag = greet ? yaju1919.appendCheckButton(main_holder,{
        title:"自動挨拶",
        change: resetSaying
    }) : () => false;
    const auto_holder = $("<div>").appendTo(main_holder);
    let auto_flag = () => false;
    if(auto) {
        auto_flag = yaju1919.appendCheckButton(auto_holder,{
            title: "定期投稿",
            change: resetSaying
        });
        RandBetween.auto.input = yaju1919.appendRandBetween(auto_holder,{
            title: "間隔",
            change: RandBetween.auto.set,
            value: [1919,2783],
            save: "auto2",
            placeholder: "[秒]",
            min: 0,
        });
    }
    const global_func_copy = {};
    ["alert","confirm","prompt"].forEach(v=>{
        global_func_copy[v] = win[v];
    });
    yaju1919.appendCheckButton(main_holder,{
        title: "ダイアログ無効化",
        change: flag => {
            for(const k in global_func_copy) win[k] = flag ? () => true : global_func_copy[k];
        }
    });
    main_holder.children().each((i,e)=>$(e).append("<br>"));
    //------- test ----------------------------------------------------------------------------------------------------------
    const showResult = str => {
        if(wait_flag) return;
        testTalk_result.empty();
        system_message(null);
        if(str) testTalk_result.text(str);
        else system_message("botからのレスポンスはありません");
    };
    const testTalk = () => {
        const obj = {};
        obj.name = "名無し";
        obj.text = testTalk_input();
        if(!obj.text.trim().length) return;
        obj.myName = "私";
        obj.log = talked_log;
        obj.wait = ()=>{
            setWait(true);
            return str => {
                setWait(false);
                showResult(str);
            };
        };
        const answer = talk(obj);
        showResult(answer);
        test_holder.find("input").val(null);
    };
    const testTalk_input = yaju1919.appendInputText(test_holder,{
        placeholder: "botとお試し対話",
        han: false,
        enter: testTalk
    });
    $("<button>",{text:"テスト対話"}).appendTo(test_holder).click(testTalk);
    const testTalk_result = $("<div>").css({backgroundColor:"blue",color:"white"}).appendTo(test_holder);
    const system_elm = $("<div>").css({backgroundColor:"yellow",color:"red"}).appendTo(test_holder);
    const getNowTime = () => new Date().toString().match(/[0-9]{2}:[0-9]{2}:[0-9]{2}/)[0];
    const system_message = text => text ? system_elm.text(text+' ('+getNowTime()+')') : system_elm.empty();
    //-----------------------------------------------------------------------------------------------------------------
    const setSub_holder = key => {
        sub_holder.empty();
        if(!bot_appendElementFunc_List[key]) return;
        sub_holder.append(bot_appendElementFunc_List[key]());
    };
    //-----------★絶対発言するマンの改良------------------------------------------------------------------------------------------------------
    const PriorityList = {};
    bot_appendElementFunc_List["★絶対発言するマン"] = () => {
        const hhh = $("<div>").append("★絶対発言するマンの優先順位<br>(値が大きいほど優先度高、0は除外)");
        for(const k in PriorityList){
            const h = $("<div>").appendTo(hhh);
            PriorityList[k].f = yaju1919.appendInputNumber(h,{
                value: PriorityList[k].v,
                placeholder: "0~",
                min: 0,
                save: "優先順位_" + k
            });
            PriorityList[k].e = $("<span>",{text:k});
            h.append(' : ').append(PriorityList[k].e).append(" ");
            if(bot_appendElementFunc_List[k]) {
                yaju1919.appendCheckButton(h,{
                    title: "表示",
                    change: flag => flag ? h2.show() : h2.hide()
                });
                const h2 = $("<div>").appendTo(h).hide().append(bot_appendElementFunc_List[k]()).append("<br>");
            }
        }
        return hhh;
    };
    //-----------------------------------------------------------------------------------------------------------------
    setTimeout(updateSelect, 1000);
    setInterval(interval, 500);
    return main_holder;
};