node.jsstringstring-searchstring-algorithm

I would like my bot to delete the message that contains a keyword or that contains similar characters


in my bot I have implemented a keyword filter that the bot reviews in each message that is written in the chat, until now it works, but I want to improve it, for reasons of respect I will not put words here, so I will put some others example,

The bot detects if you write for example "vulgar", "badword", "hello"

But what I want to achieve is to detect if they write "hellooo", "vuulgarr", vulg4rr"

This is my base where I have the words stored:

badwords.js

var words = ["vulgar", "vulg4r", "hello", "badword4", "badword5"]

module.exports = words;

This is my function that checks if a bad word comes on the way, split any words and then deletes the message if it finds a result, with indexOf()

index.js

const _ = require('lodash');
const badwords = require('./badwords');

/**
 * Functions
 */

// compares every word to badWords array from badWords.js
function checkWord(word) {
    return badwords.indexOf(word) > -1;
}

/**
 * Main Module
 */

module.exports = function (self, nick, channel, message) {
    'use strict';
    message = message.toLowerCase();
    message = message.split(' ');
    nick = nick;
    channel = channel.toLowerCase();
    for (var i = 0, len = message.length; i < len; i++) {
        if (checkWord(message[i])) {
            self.send('.ban', channel, nick);
        }
    }
}

Any idea to improve it?, thank's


Solution

  • A more complicated method

    We can have two pointers on both strings to compare, but skipping offsets upon duplicates:

    function checkString(message, keyword) {
        while(message.length > 0) {
            if(checkPrefix(message, keyword)) return true
            message = message.substr(1)
        }
    }
    function checkPrefix(message, keyword) { // keyword is one of the keywords
        let om = 0, ok = 0
        while (true) {
            if (ok >= keyword.length)
                return true // we have finished reading keyword, and everything matched
            if(om >= message.length)
                return false // message is shorter than keyword
            while (om + 1 < message.length && message.charAt(om) === message.charAt(om + 1))
                om++ // skip consecutive repetitions in message
            while (ok + 1 < keyword.length && keyword.charAt(ok) === keyword.charAt(ok + 1))
                ok++ // skip consecutive repetitions in keyword
            if (message.charAt(om) !== message.charAt(ok)) return false // encountered an inconsistent character
        }
    }
    

    A simpler method

    Just scan the repetitions in a string and delete them first.

    function removeDuplicates(string) {
        for (let i = 0; i < string.length - 1; ) {
            if (string.charAt(i) === string.charAt(i + 1)) {
                string = string.substr(0, i) + string.substr(i + 1) // skip string[i]
            } else {
                i++ // not duplicate, proceed to next pair
            }
        }
    }
    

    Then you can compare directly:

    removeDuplicates(message).indexOf(removeDuplicates(keyword)) !== -1
    

    You can apply it like this:

    for (const part in message.split(" ")) {
        for (word in words) {
            if (removeDuplicates(part).indexOf(removeDuplicates(word)) !== -1)
                self.send(".ban", ...)
                break
        }
    }