Дерзкий telegram бот

от автора

Недавно, в попытках разобраться с nlp, мне пришла идея написать простого telegram бота, который будет разговаривать, как дерзкий гопник. То есть:

  • давать ответ по слову-триггеру, как "хочу", "короче", "нет" и т.д.;
  • отвечать дерзким вопросом на вопрос;
  • отвечать нецензурной рифмой;
  • если ничего не подходит и бот в замешательстве, отвечать злой фразой.

Для имплементации был выбран JavaScript с ES6 и Flow. Возможно, Python подошёл бы лучше, так как под него существует больше стабильных и проверенных библиотек для nlp. Но для JS есть Az.js, которого вполне хватило.

Для работы с Telegram API был использован node-telegram-bot-api.

TLDR: бот, исходный код

Осторожно, под катом присутствует нецензурная речь и детали реализации!

Часть с реализацией работы с Telegram API не сильно интересная, и про это уже написано множество статей, и её я опущу.

Начну сразу с того, как бот пытается найти подходящий ответ. Первый метод поиска ответа – слово-триггер:

user: Хочу новую машину!
bot: Хотеть невредно!

Для начала мы имеем список пар [regexp, ответ]:

const TRIGGERS = [   [/^к[оа]роч[ье]?$/i, 'У кого короче, тот дома сидит!'],   [/^нет$/i, 'Пидора ответ!'],   [/^хо(чу|тим|тят|тел|тела)$/i, 'Хотеть невредно!'], ];

Потом мы должны разбить сообщение от пользователя на слова:

const getWords = (text: string): string[] =>   Az.Tokens(text)     .tokens     .filter(({type}) => type === Az.Tokens.WORD)     .map(({st, length}) => text.substr(st, length).toLowerCase());

Пройти по всем триггерам и вернуть возможные ответы:

const getByWordTrigger = function*(text: string): Iterable<string> {   for (const word of getWords(text)) {     for (const [regexp, answer] of constants.TRIGGERS) {       if (word.match(regexp)) {         yield answer;       }     }   } };

Вышло очень просто. Теперь пришло время второго метода поиска ответа – отвечать дерзким вопросом на вопрос:

user: Когда мы уже пойдём домой?
bot: А тебя ебёт?

Для того чтобы определить, является ли сообщение вопросом, мы должны проверить его на наличие вопросительного знака в конце и на наличие вопросительных слов, как "когда", "где" и т.д.:

const getAnswerToQuestion = (text: string): string[] => {   if (text.trim().endsWith('?')) {     return [constants.ANSWER_TO_QUESTION];   }    const questionWords = getWords(text)     .map((word) => Az.Morph(word))     .filter((morphs) => morphs.length && morphs[0].tag.Ques);    if (questionWords.length) {     return [constants.ANSWER_TO_QUESTION];   } else {     return [];   } };

Так, в случае вопроса, бот вернёт захардкоженый constants.ANSWER_TO_QUESTION.

Третий метод поиска ответа – ответ нецензурной рифмой. Этот метод наиболее сложный:

user: хочу в Австрию!
bot: хуявстрию
user: у него есть трактор
bot: хуяктор

Вкратце: мы просто заменяем первый слог существительного или прилагательного на "ху" и трансформированную гласную из слога, как "о" → "ё", "а" → "я" и т.д.

Для начала мы должны уметь получать первый слог слова. Это несложно:

const getFirstSyllable = (word: string): string => {   const result = [];    let readVowel = false;    for (const letter of word) {     const isVowel = constants.VOWELS.indexOf(letter) !== -1;      if (readVowel && !isVowel) {       break;     }      if (isVowel) {       readVowel = true;     }      result.push(letter);   }    return result.join(''); };

Потом нужно заменять первый слог на "ху" + гласную, если это возможно:

const getRhyme = (word: string): ?string => {   const morphs = Az.Morph(word);   if (!morphs.length) {     return;   }    const {tag} = morphs[0];   if (!tag.NOUN && !tag.ADJF) {     return;   }    const syllable = getFirstSyllable(word);   if (!syllable || syllable === word) {     return;   }    const prefix = constants.VOWEL_TO_RHYME[last(syllable)];   const postfix = word.substr(syllable.length);    return `${prefix}${postfix}`; };

И, наконец, возвращать все возможные рифмы для слов из сообщения:

const getRhymes = (text: string): string[] =>   getWords(text)     .map(getRhyme)     .filter(Boolean)     .reverse();

Последний метод поиска ответа – отвечать в замешательстве грубой фразой:

user: wtf
bot: Чё?

Этот метод более чем простой, поэтому будет реализован в агрегирующей все методы функции:

export default (text: string): string[] => {   const answers = uniq([     ...getByWordTrigger(text),     ...getAnswerToQuestion(text),     ...getRhymes(text),   ]);    if (answers.length) {     return answers;   } else {     return constants.NO_ANSWERS;   } };

И это всё. Бот, исходный код.

ссылка на оригинал статьи https://habrahabr.ru/post/327586/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *