Pull to refresh

Джаббер чат на веб-странице

Reading time 3 min
Views 32K
Прочитав пост на хабре про онлайн чат для сайта через джаббер, мне стало интересно — а как оно работает и как такое можно сделать самому, без готовых приложений. В итоге у меня получилась очень простая заготовка «чата для сайта через джаббер». К сожалению у меня нет выделенного сервера с линуксом для тестов, поэтому был использован локальный компьютер с Win7 (и сервером Apache).

Как это вообще должно работать: пользователь заходит на сайт, и видит окошко, куда можно разговаривать. После того как пользователь послал сообщение, оно прилетает на указанный джаббер аккаунт. Получатель этого сообщения может написать ответ и оно придёт посетителю сайта.
Что для этого нужно:
  • Jabber сервер, можно публичный, можно локальный. Я выбрал Openfire и установил его локально. Сервер должен поддерживать Bosh — XEP-0124: Bidirectional-streams Over Synchronous HTTP, об этом чуть позже.
  • JS библиотека, которая будет реализовывать джаббер-клиент на сайте. Я взял Strophe. Это достаточно низкоуровневая библиотека, в которой нет функций типа «ПослатьСообщение(Куда, Текст)». Для достижения нужных действий нужно вручную составлять команды джаббер серверу (в XML). Удобные средства для создания XML в Strophe есть :)

BOSH


JS не умеет создавать TCP соединения с другим сервером/клиентом, что необходимо для реализации джаббер-клиента. JS может посылать только HTTP запросы. Поэтому нужен специальный механизм, который позволит работать с TCP соединениями посредством HTTP. Это и есть BOSH.

По-умолчанию BOSH в Openfire включён и имеет адрес localhost:7070/http-bind. Но если указать этот адрес при соединении, ничего не выходит. Проблема хорошо описана здесь, что бы заработало нужно написать редирект для апача и раскомментировать модули proxy_module и proxy_http_module:

httpd.conf:
ProxyRequests Off
ProxyPass /http-bind http://127.0.0.1:7070/http-bind/
ProxyPassReverse /http-bind http://127.0.0.1:7070/http-bind/

Чат на сайте


Итак, всё очень просто, устанавливаем Openfire, создаём пользователя, например, site, и создаём тестовую страницу (в качестве основы я брал строфовский пример echobot). На странице есть поле, куда пользователь пишет своё сообщение, и поле с историей чата.

Пишем обработчик загрузки страницы, который будет логинится на джаббер сервер:
$(function () {
    connection = new Strophe.Connection('/http-bind');
    connection.connect('site@r1c', 'site', onConnect);
});

В обработчике onConnect мы добавляем «слушателя» на события, связанные с приходом сообщений (в случае успешного логина):
function onConnect(status){
    if (status == Strophe.Status.CONNECTING) {
        log('Strophe is connecting.');
    }
    else if (status == Strophe.Status.CONNFAIL) {
        log('Strophe failed to connect.');
    }
    else if (status == Strophe.Status.DISCONNECTING) {
        log('Strophe is disconnecting.');
    }
    else if (status == Strophe.Status.DISCONNECTED) {
        log('Strophe is disconnected.');
    }
    else if (status == Strophe.Status.CONNECTED) {
        log('Strophe is connected, ' + connection.jid);
        
        connection.addHandler(onMessage, null, 'message', null, null, null);
        connection.send($pres().tree());
    }
}

Обработчику onMessage приходят XML данные. Разбираем их что бы узнать текст сообщения, кто отправитель и т.д.:
function onMessage(msg) {
    var to = msg.getAttribute('to');
    var from = msg.getAttribute('from');
    var type = msg.getAttribute('type');
    var elems = msg.getElementsByTagName('body');

    if (type == "chat" && elems.length > 0) {
        var body = elems[0];
    
        AddText(Strophe.getText(body), 'in');
    }

    // we must return true to keep the handler alive.  
    // returning false would remove it after it finishes.
    return true;
}

Обработчик кнопки «Отправить» формирует XML с необходимыми данными и просит Strophe отправить их. В качестве принимающего используется пользователь admin:
function Send(message) {
    var msg = $msg({to: 'admin@r1c', from: connection.jid, type: 'chat'}).c('body').t(document.URL + '\n' + message);
    connection.send(msg.tree());
    
    AddText(message, 'out');
    $('input#message').val('');
}

Итог



В итоге получился очень простой набросок, позволяющий общаться по джабберу через веб-страницу. Мне было интересно узнать как это работает в принципе, поэтому я не стал копать дальше. Что-то мне подсказывает там есть куча подводных камней :)

Можно придумать ещё очень много усовершенствований. Например:
  • Нормальный интерфейс чата на сайте :)
  • Автоматическая регистрация новых пользователей на джаббер-сервере, если в этом есть необходимость. Для этого можно использовать In-band registration — регистрация через XML команды джаббера, а не через веб-интерфейс.
  • Сохранение состояния чата при переходе между разными страницами сайта
  • Сохранение истории чата на сервере
  • и т.д.
Tags:
Hubs:
+44
Comments 19
Comments Comments 19

Articles