<commit_before>import Plugin from "./../Plugin";
import Blather from "blather";

export default class Markov extends Plugin {

    static get plugin() {
        return {
            name: "Markov",
            description: "Generates random text.",
            needs: {
                database: true
            }
        };
    }

    m = null;
    rate = 0;

    start() {
        if (this.db) {
            this.m = Blather.destringify(JSON.stringify(this.db));
        } else {
            this.m = new Blather();
        }
    }

    onText(message, reply) {
        if (message.text !== "/markov") {
            this.m.addFragment(message.text);
            if (Math.random() > this.rate) return;
        }
        reply({
            type: "text",
            text: this.m.generateFragment()
        });

        this.db = JSON.parse(this.m.stringify());
    }
}<commit_msg>Add seed to Markov<commit_after>import Plugin from "./../Plugin";

function arraySample(arr) {
    return arr[Math.floor(Math.random() * arr.length)];
}

class Blather {
    constructor({
        isStart = (key, index) => index === 0,
        clean = textArray => textArray.join(' '),
        split = text => text.split(/\s+/),
        depth = 2,
        joiner = "<|>",
        dictionary = {starts: [], chains: {}}
    }) {
        this.isStart = isStart;
        this.clean = clean;
        this.split = split;
        this.depth = depth;
        this.joiner = joiner;
        this.dictionary = dictionary;
    }

    addFragment(text) {
        const tokens = this.split(text);
        const limit = tokens.length - 1 - this.depth;
        for (let i = 0; i < tokens.length; i++) {
            if (i > limit) return;

            const key = tokens.slice(i, i + this.depth).join(this.joiner);

            if (this.isStart(key, i))
                this.dictionary.starts.push(key);

            this.dictionary.chains[key] = this.dictionary.chains[key] || [];
            this.dictionary.chains[key].push(tokens[i + this.depth]);
        }
    }

    // start is an array of words with which to start
    generateFragment(start = arraySample(this.dictionary.starts).split(this.joiner)) {
        return this.fill(start, this.shouldStopFragment);
    }

    fill(chain, stopCondition) {
        let key = chain.slice(chain.length - this.depth).join(this.joiner);

        while (this.dictionary.chains[key] && !stopCondition(chain)) {
            chain.push(arraySample(this.dictionary.chains[key]));
            key = chain.slice(chain.length - this.depth).join(this.joiner);
        }

        return this.clean(chain);
    }

    stringify() {
        return JSON.stringify({
            depth: this.depth,
            joiner: this.joiner,
            dictionary: this.dictionary
        });
    }

    shouldStopFragment(chain) {
        return chain.length >= 1000;
    }

    static destringify(stringified) {
        return new Blather(JSON.parse(stringified));
    }
}

export default class Markov extends Plugin {

    static get plugin() {
        return {
            name: "Markov",
            description: "Generates random text.",
            help: "/markov, or `/markov <seed>`",
            needs: {
                database: true
            }
        };
    }

    m = null;
    rate = 0.02;

    start() {
        if (this.db) {
            this.m = Blather.destringify(JSON.stringify(this.db));
        } else {
            this.m = new Blather();
        }
    }

    onText(message, reply) {
        // Take advantage of this to sync the db to memory
        this.db = {
            depth: this.m.depth,
            joiner: this.m.joiner,
            dictionary: this.m.dictionary
        };

        this.m.addFragment(message.text);
        if (Math.random() > this.rate) return;
        reply({
            type: "text",
            text: this.m.generateFragment()
        });
    }

    onCommand({message, command, args}, reply) {
        if (command !== "markov") return;
        reply({
            type: "text",
            text: this.m.generateFragment((args.length > 0) ? args : undefined)
        });
    }
}
{'fraction_non_alphanumeric': 0.13714562636297553, 'fraction_numerical': 0.003149987884661982, 'mean_word_length': 1.685751463890696, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1033410', 'n_tokens_mistral': 1248, 'n_tokens_neox': 1183, 'n_words': 264}