Создаём политического Twitter-бота с помощью Node.js и StdLib

Политический Twitter-бот

В мире политики пропаганда — это незаменимое оружие на поединке, где Twitter выступает в роли ринга, а Twitter-боты — читы, которые дают участнику превосходство. У команды StdLib нет политических мотивов, но есть страсть — создавать ботов. Джейден Трюдо, эксцентричный «кандидат» в премьер-министры Канады, — их новый проект, с которым легко ознакомиться при помощи сервиса StdLib Sourcecode. И подобное чудо инженерии создаётся в считанные минуты!

Twitter-бот обращается к народным массам, поэтому для создания идеального политика было решено совместить мудрость Джейдена Смитта:

Примечание переводчика «Я Постоянно Строю Пирамиды».

и нравственность Джастина Трюдо:

Примечание переводчика «Желаю Супер Хэллоуина!»

Если точнее, то целью было создать бота, который периодически публикует в Twitter сообщения в стиле Джейдена Смитта и Джастина Трюдо. В результате такой комбинации получаются чудесные твиты вроде:

Примечание переводчика «Я — Верховный Судья Утончённости».

Этот проект реализован с помощью цепей Маркова, которые также используются в алгоритме ранжирования страниц Google. На сайте StdLib программа доступна для тестирования через API и непосредственно со страницы сайта. Перед запуском в параметрах можно указать другие Twitter-аккаунты, чтобы при создании записей использовать их.

«Собираюсь к вам на выборы», — Джейден Трюдо.

Определение цепи Маркова

Чарльз Гринстед и Лори Снелл в книге «Introduction to Probability» описывают цепь Маркова как набор состояний S = {s_1, s_2, ..., s_r}. Процесс начинается в одном из этих состояний и последовательно двигается из одного в другое. Каждое перемещение называется шагом. Если цепь размещена в состоянии s_i, то потом переместится в s_j с вероятностью p_ij. Эта вероятность не зависит от состояний, в которых цепь уже побывала.

Цепь Маркова — это математическая модель, которая переходит из одного состояния в другое, не сохраняет информацию о предыдущих состояниях и обрабатывает только текущее состояние. Рассмотрим это определение на примере генератора текста:

  1. Разделяем корпус (текст) на звенья (слова и знаки препинания).
  2. Строим таблицу частот. Для каждого уникального звена в корпусе задаём ключ: каждому ключевому слову соответствует список слов, которые следуют за ним, с указанием частоты. Для начала и конца предложения устанавливаются специальные ключи. Это гарантирует, что сформированные предложения будут начинаться и заканчиваться приемлемыми словами.
  3. Выбираем точку отсчёта — специальное начальное звено.
  4. Находим случайным образом новое звено, следующее в таблице частот за предыдущим. Вероятность выбора каждого звена должна быть пропорциональна тому, как часто оно встречается после ключевого звена. Выбранное звено становится новым состоянием цепи Маркова. Повторяем этот пункт необходимое количество раз.

Примечание переводчика Наглядно эти принципы описаны в нашей статье о создании генератора текста на основе цепей Маркова.

Реализация

Теперь, когда появилось представление о дальнейших действиях, пора приступать. Сначала обработаем твиты. Это легко сделать с помощью пакета Twit.

const twit = require('twit');

const twitter = new twit({
  consumer_key: process.env.TWITTER_CONSUMER_KEY,
  consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
  access_token: process.env.TWITTER_ACCESS_TOKEN,
  access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
});

const getTweets = user => {
  const _getTweets = (user, last_id = null) => {
    let params = {
      screen_name: user,
      count: 200,
      include_rts: false
    };

    if (last_id) {
      params.max_id = last_id;
    }

    return twitter
      .get('statuses/user_timeline', params)
      .then(response => {
        if (depth > 10) {
          return tweets;
        }

        tweets = tweets.concat(
          response.data
            .filter(tweetData => tweetData.lang === 'en')
            .map(tweetData => tweetData.text)
        );
        depth++;
        last_id = response.data[response.data.length - 1].id_str;
        return _getTweets(user, last_id);
      })
      .catch(error => {
        return error;
      });
  };

  let tweets = [];
  let depth = 1;
  return _getTweets(user).then(response => {
    return tweets;
  });
};

module.exports = getTweets;

Преобразуем полученные данные в массив цепей. Для этого убираем из твитов лишнее (URL-адреса, смайлики, неправильно построенные предложения) и разбиваем их на слова, из которых формируется таблица частот.

Записи таблицы частот могут быть обработаны по-разному. В начале, с вероятностью 50/50 слова вроде «our» («наш») или «we» («мы») будут выбраны как специальные начальные слова. Предположим, что выбрано слово «our». Тогда с вероятностью 2/5 будут выбраны слова «future» («будущее») или «differences» («различия») и вероятностью 1/5 — «relationship» («отношения»). Процесс повторяется до тех пор, пока не будет создана цепочка вроде такой:

__START -> our -> future -> office -> __END

Готово. Результат работы скрипта доступен в Twitter на странице Джейден Трюдо, а полный исходный код на странице GitHub. Конечно, возможна установка дополнительных опций. Например, для формирования нескольких предложений за раз требуется добавить границы с __END до __START и убедиться, что предложение закончено.

Я построю своего политического бота!

Для желающих написать Twitter-бота самостоятельно исходный код размещен на сайте StdLib. На странице по ссылке есть возможность даже развернуть бота непосредственно через браузер!

1. Нажимаем кнопку «Create Service from Source» («Создать службу из источника») и заполняем форму:

Первое поле — токен библиотеки StdLib, который оставляем без изменений. Остальные четыре находим на странице управления приложениями Twitter.

2. Нажимаем кнопку «Create New App» («Создать новое приложение») и заполняем форму:

3. Нажимаем кнопку «Create» («Создать») и на экране видим четыре ключа.

4. Копируем ключи в форму и нажимаем кнопку «Deploy» («Развернуть»).

5. Чтобы увидеть код локально и внести изменения нужен CLI, который установим с помощью NPM, выполнив команду в терминале:

npm install lib.cli -g

6. Создаём рабочее пространство StdLib и получаем код, развёрнутый на предыдущем шаге:

$ mkdir stdlib-workspace
$ cd stdlib-workspace
$ lib init
$ lib get /[ваш-логин]/twitter-bot

7. По умолчанию бот использует цепи Маркова для формирования твитов. Чтобы переключить на другой метод, открываем файл __main__.js:

const lib = require('lib');

/**
* @param {array} twitterHandles
* @returns {any}
*/
module.exports = (twitterHandles, context, callback) => {
  if (context.user && context.user.username !== context.service.path[0]) {
    return callback(new Error('Invalid library token'));
  }
  // Change this if you want to generate tweets your own way 
  lib.steve['twitter-markov-chain'](twitterHandles)
    .then(tweet => {
      lib[`${context.service.identifier}.postTweet`](tweet).then(result => {
        return callback(null, result);
      });
    })
    .catch(error => {
      return callback(error);
    });
};

В 12-й строке изменяем вызываемую функцию lib.steve[twitter-markov-chain] на свою.

8. Собираем бота заново, выполнив команду:

$ lib release

Бот готов! Политический бот — это лишь одно из возможных применений описанных технологий: цепей Маркова, библиотеки StdLib и создания Twitter-бота.

Перевод статьи «Create a Twitter Politician Bot with Markov Chains, Node.js and StdLib»

Подобрали два теста для вас:
— А здесь можно применить блокчейн?
Серверы для котиков: выберите лучшее решение для проекта и проверьте себя.