Недавно, в попытках разобраться с 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;
  }
};

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

Поделиться с друзьями
-->

Комментарии (25)


  1. kesn
    28.04.2017 16:19
    +20

    Ждём, когда вы начнёте изучать нейронные сети и вырастите гопника уже на них :)


    1. nvbn
      28.04.2017 16:30
      +3

      Если бы был большой датасет пар `сообщение: ответы`, то было бы легко =)

      Жалко что в Mechanical Turk нет русскоязычных.


      1. mizhgun
        28.04.2017 22:01
        +2

        > Жалко что в Mechanical Turk нет русскоязычных.

        Есть Толока вообще-то.


      1. Antelle
        29.04.2017 00:11
        +1

        Можно попробовать двач вконтакт, какие-нибудь затролленные паблики, теоретически должно быть то, что надо.


      1. alex4321
        29.04.2017 14:47

        reactor.cc/tag/anon :-)
        Но это из другой области.


        1. alex4321
          29.04.2017 14:50

          Хотя, чего-то анон теперь ещё большая хрень, чем при последнем моём попадании в него.


  1. klylex
    28.04.2017 16:23
    +6

    Осталось сделать бота «Диванный эксперт».


    1. Alexey_mosc
      28.04.2017 19:50

      Плюсую! ))) «на форуме говорят, что ТОЧНО американцы на луну не летали».


  1. Ravebinovich
    28.04.2017 16:32
    +3

    Тренер ведения диалога с аборигенами неблагополучных районов. Полезный бот.

    P.S. Можно добавить функционал, чтоб он ещё докапывался в духе «Чё такой дерзкий?», «Ты с какого района?», «Поясни за шмот.», «А чё волосатый?», «Мелочь есть?» ну итд.


    1. klylex
      28.04.2017 16:41
      +3

      После фразы «Мелочь есть?» необходимо предоставить номер свой карты, пин-код и доступы в личный кабинет онлайн-банка (если имеется).


      1. Caravus
        28.04.2017 16:48
        +9

        (если имеется)

        «А если найду?»


        1. klylex
          28.04.2017 16:49
          +3

          Да, после этого уже выезжают коллекторы.


  1. Assada
    28.04.2017 16:41
    +2

    Если бот отвечает на каждое сообщение — в группу его не добавить. Было бы хорошо чтобы он внезапно и лишь иногда отвечал участникам группы. Так делает бот «Виталя» (@P06at_E60boT). Он отвечает только если к нему обращаются по имени «Веталь, Виталя, etc..» ну или иногда встревает в разговор одним сообщением.

    Смотреть ответы
    image
    image


  1. ua9msn
    28.04.2017 16:43
    +14

    Нужно было писать на YoptaScript


  1. Alexey_mosc
    28.04.2017 19:48
    +1

    «Чики-брыки», «ты чё чёкаешь», «стоять-бояться», «Лысого знаешь?» Развивайте идею и потом код в студию ) Буду на автоответчик ставить )))


  1. Greesha
    28.04.2017 20:49
    +2

    Идея такого искусственного интеллекта постоянно приходит в голову разным пытливым умам. Одна из известных реализаций — Мыши Дебиляриуса.

    А мой одногруппник ещё в 1988 году написал интерактивный гороскоп на языке REXX (под VM/370), который вежливо задавал вопросы, а потом выдавал на основании полученных ответов такую характеристику, что этого моего одногруппника неоднократно пытались побить. Но я уверен, что подобные вещи делали и намного раньше.


  1. Alex_ME
    28.04.2017 21:18
    +1

    Забавано!


    Мой друг descine делал бота на основе библиотеки chatterbot (хоть и с акцентом на голосовое взаимодействие). Вдохновившись, я прикрутил к этому боту VK API и отправил обучаться в чаты. Получилось весьма… нецензурно.


    А потом, спустя пару дней, я часа 3 получал капчу на каждую отправку сообщения.


  1. Kanumowa
    28.04.2017 23:46
    +1

    Например можно отсюда фраз насобирать: versusbattleru


    1. Alexey_mosc
      29.04.2017 22:41

      Адище.


  1. antonksa
    29.04.2017 04:01

    Пообщался)))


  1. cats_shadow
    29.04.2017 12:39
    +2

    Чойта он какой-то не дерзкий. :) Всё время в однословную рифму сваливается. :)


  1. afanasiy_nikitin
    29.04.2017 12:42
    +8

    Искусственный интеллект, который мы заслужили.


  1. mwizard
    29.04.2017 16:37
    +4

    image


    1. n_demitsuri
      02.05.2017 11:24

      https://t.me/aaaaarobot


  1. Alexey_mosc
    29.04.2017 22:53

    Пообщался с ним. Он не то, что поддатый, а в дупель пьяный гопник. По ощущениям ))) Но забавно…