...

пятница, 3 июня 2016 г.

Шестое чувство Facebook

Расширение для Chrome показывает, когда кто-то набирает текст


Некоторые люди слишком много времени проводят в социальных сетях. Настолько много, что у них уже возникает зависимость. Один из таких — программист Александр Кирзенберг (Alexandre Kirszenberg), который к тому же любит копаться во внутренностях Facebook — в коде JavaScript, отвечающем за пользовательский интерфейс и коммуникации.

«Пару месяцев назад я задумался о маленьком статусном индикаторе, который показывает, когда один из ваших друзей набирает вам текст, — пишет Александр. — Такое маленькое расширение UI выдаёт много информации о собеседнике. Если индикатор несколько раз загорается и тухнет, это говорит о нерешительности. Если он загорелся надолго, кто-то пишет вам большое эссе. И нет ничего хуже того мучительного чувства, когда индикатор тухнет и больше не загорается».

Первым делом Александр решил найти, присылает ли Facebook уведомления о том, что кто-то набирает сообщения в Facebook Messenger, даже если у вас не запущен этот Facebook Messenger. Оказалось, что присылает.

Всем френдам рассылается событие typ длинного опроса /pull. Небольшое исследование показало, что событие действительно рассылается для каждого чата, даже если получатель не открывал его и никогда не получал.

Так появилось расширение Facebook Sixth Sense для браузера Chrome. Оно показывает прямо в браузере, когда кто-то набирает сообщение в Facebook.

Для создания этого расширения программист использовал недокументированные Facebook API. Пришлось разбираться с кодом JavaScript, который Facebook безжалостно минифицирует, превращая в мешанину символов.

Сначала он выяснил, какие модули Facebook импортирует через программный интерфейс системы организации модулей Asynchronous Module Definition для асинхронной загрузки (у Facebook собственная реализация AMD). Модули и зависимости определяются стандартной функцией __d(name, dependencies, factory). Есть также require и requireLazy для импорта модулей.

Проще всего посмотреть, как это работает, в консоли браузера (хотя Facebook строго запрещает это делать). Похоже, там работают серьёзные ребята, с ними лучше не шутить.

Но мы всё-таки осмелимся.

Как видим, Facebook всегда загружает последнюю версию React — отличной библиотеки компонентов пользовательских интерфейсов. Facebook довольно интенсивно использует React по всему сайту. В коде Facebook более 15 000 компонентов React (по состоянию на октябрь 2015 года).

В исходном коде можно поискать __d( и посмотреть список модулей, доступных для импорта. Для главной страницы там всего-то 3000 модулей.

В чате Facebook Messenger, разумеется, тоже используются компоненты React. Нам нужно перехватить нотификации о наборе текста. Для более детального изучения кода Александр Кирзенберг рекомендует использовать инструмент React Developer Tools.

После установки этого расширения в инструментах разработчика Chrome появляется новая вкладка React. На ней выделяем чат.

Здесь среди различных компонентов Facebook Messenger ищем индикатор набора текста, он находится между <ChatTabComposerContainer /> и <MercuryLastMessageIndicator />.

Поиск __d('ChatTyping в кодовой базе React находит два модуля: ChatTypingIndicator.react.js и ChatTypingIndicators.react.js. Это именно то, что нам нужно, пишет Кирзенберг. Он замечает, что некоторые модули подгружаются по мере необходимости, так что ChatTypingIndicators.react.js можно обнаружить только со второго раза.

Вот его код.

function() {
  var k = c('MercuryThreadInformer').getForFBID(this.props.viewer)
    , l = c('MercuryTypingReceiver').getForFBID(this.props.viewer);
  this._subscriptions = new (c('SubscriptionsHandler'))();
  this._subscriptions.addSubscriptions(
    l.addRetroactiveListener(
      'state-changed',
      this.typingStateChanged
    ),
    k.subscribe(
      'messages-received',
      this.messagesReceived
    )
  );
},

А именно, нас интересует вызов c('MercuryTypingReceiver').

В консоли можно посмотреть, как он работает.

> MercuryTypingReceiver.getForFBID
// function (i){var j=this._getInstances();if(!j[i])j[i]=new this(i);return j[i];}
> MercuryTypingReceiver.get
// function (){return this.getForFBID(c('CurrentUser').getID());}

Для проверки, как работает статусный индикатор, Александр использовал приложение Messenger на собственном смартфоне, чтобы отправить себе на ПК соответствующие события и словить их в консоли.

Более подробно изучив код, он нашёл ещё два полезных модуля MercuryThreads и ShortProfiles. Первый получает всю информацию о треде Messenger по его идентификатору, второй делает то же самое по профилю.

В общем, после всех изысканий вот как выглядит окончательный код расширения для Chrome, всего 40 строчек.

function getUserId(fbid) {
  return fbid.split(':')[1];
}

requireLazy(
  ['MercuryTypingReceiver', 'MercuryThreads', 'ShortProfiles'],
  (MercuryTypingReceiver, MercuryThreads, ShortProfiles) => {

    MercuryTypingReceiver
      .get()
      .addRetroactiveListener('state-changed', onStateChanged);

    // Called every time a user starts or stops typing in a thread
    function onStateChanged(state) {

      // State is a dictionary that maps thread ids to the list of the
      // currently typing users ids'
      const threadIds = Object.keys(state);

      // Walk through all threads in order to retrieve a list of all
      // user ids
      const userIds = threadIds.reduce(
        (res, threadId) => res.concat(state[threadId].map(getUserId)),
        []
      );

      MercuryThreads.get().getMultiThreadMeta(threadIds, threads => {
        ShortProfiles.getMulti(userIds, users => {
          // Now that we've retrieved all the information we need
          // about the threads and the users, we send it to the
          // Chrome application to process and display it to the user.
          window.postMessage({
            type: 'update',
            threads,
            users,
            state,
          }, '*');
        });
      });

    }
  }

);

Неплохой такой хак, слегка раскрывающий внутренности Facebook.

Исходный код расширения опубликован на Github.

Кстати, по временным меткам из мессенджера Facebook можно даже отслеживать режим сна своих френдов (исходный код).

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

    Let's block ads! (Why?)

    Комментариев нет:

    Отправить комментарий