Простой пример использования gloox-1.0 для написания XMPP-бота
(Форматирование страницы не завершено / 31 марта 2012 года)
Исходник: Basic gloox Tutorial / March 23, 2012 — Anders Schau Knatten

Перевод и адаптация: Михайлов Алексей aka iboxjo (iboxjo.h1.ru)

 На странице проекта gloox доступно руководство, однако пример приведённый на главной странице проекта является устаревшим, именно по этой причине и написано данное, небольшое руководство. Оно основано на старом примере, однако расширено и обновлено для работы с библиотекой gloox версии 1.0.

 Поскольку вы читаете эти строки, скорее всего вы уже знаете, что gloox является популярной библиотекой для разработки программ использующих протокол XMPP, ранее известный как jabber. В этом небольшом учебнике, будет рассказано, как создать XMPP-бот, который подключается к серверу (используя TLS), прослушивает сообщения и отвечает на них самым раздражающим образом. Если вам просто необходим код, вы можете взять bot.cpp с GitHub, и следовать инструкциям в заголовке файла.

Для работы вам потребуется использовать следующие включения и пространства имён:

#include <iostream>
#include <string>
#include <gloox/client.h>
#include <gloox/message.h>
#include <gloox/messagehandler.h>
#include <gloox/connectionlistener.h>

using namespace std;
using namespace gloox;



В первую очередь, нам необходимо создать MessagesHandler. Он будет подключаться к серверу и управлять входящими соединениями.

01 class Bot : public MessageHandler {
02 public:
03    Bot() {
04        JID jid("bot@localhost");
05        client = new Client( jid, "botpwd" );
06        connListener = new ConnListener();
07        client->registerMessageHandler( this );
08        client->registerConnectionListener(connListener);
09        client->connect(true);
10    }

Строка 4: ID нашего пользователя. В этом примере я использовал локальный XMPP сервер, с ранее созданным пользователем "bot".
Строка 5: Здесь создаётся клиент с нашим ID и паролем.
Строка 6: Нам необходим ConnectionListener для управления соединениями, однако мы вернёмся к этому позже.
Строка 7: Client необходим MessagesHandler для обработки входящих сообщений. Поскольку Bot наследует MessagesHandler, мы просто передадим ему this.
Строка 8: ConnectionListener так же должен быть зарегистрирован с Client
Строка 9: И наконец мы соединяемся с XMPP-сервером. Существует два способа подключения, блокирующее и не-блокирующее. В данном примере мы используем блокирующее соединение. Если вы используете неблокирующее соединение, вам необходимо вызывать Client::recv() для приёма данных через определённые промежутки времени.

Для обработки сообщений, Bot'у необходимо реализовать метод handleMessages:

1 virtual void handleMessage( const Message& stanza, MessageSession* session = 0 ) {
2    cout << "Received message: " << stanza << endl;
3    Message msg(stanza.subtype(), stanza.from(), "Tell me more about " + stanza.body() );
4    client->send( msg );
5 }

Строка 2: Печатает принятое сообщение (используя собственный operator<<).

ostream& operator<<(ostream& os, Message::MessageType type) {
    switch (type) {
        case Message::Chat:
            os << "Chat";
            break;
        case Message::Error:
            os << "Error";
            break;
        case Message::Groupchat:
            os << "Groupchat";
            break;
        case Message::Headline:
            os << "Headline";
            break;
        case Message::Normal:
            os << "Normal";
            break;
        case Message::Invalid:
            os << "Invalid";
            break;
        default:
            os << "unknown type";
            break;
    }
return os;
}


ostream& operator<<(ostream& os, const Message& stanza) {
    os << "type:'" << stanza.subtype() <<  "' from:'" << stanza.from().full() << "' body:'" << stanza.body() << "'";
    return os;
}


Строка 3: Создаёт новое сообщение для передачи тому кто прислал исходное сообщение, с прозьбой рассказать о увлекательной теме.
Строка 4: Отправляет сообщение с помощью Client'а которого мы создали в конструкторе Bot.

Так же нам необходим ConnectionListener для обработки соединений. Как минимум, он должен перекрывать три начальных виртуальных метода:

01 class ConnListener : public ConnectionListener {
02 public:
03    virtual void onConnect() {
04        cout << "ConnListener::onConnect()" << endl;
05    }
06    virtual void onDisconnect(ConnectionError e) {
07        cout << "ConnListener::onDisconnect() " << e << endl;
08    }
09    virtual bool onTLSConnect(const CertInfo& info) {
10        cout << "ConnListener::onTLSConnect()" << endl;
11        return true;
12    }
13 };

В данном случае, мы просто вывели (используя cout) имена методов, что позволило увидеть, что всё это работает. Если возникли ошибки, метод OnDisconnect () отображает код ошибки. Коды ошибок можно найти в заголовочном файле gloox.h.

Стоит обратить особое внимание на метод TLSConnect (). Он должен проверять полномочия сертификата TLS и возвращать true если они приняты. В данном примере, мы будем принимать всё что получаем.

Теперь можно создать main подобную следующей:

1 int main() {
2    Bot b;
3 }

Вы можете попробовать пообщаться с ботом используя любой клиент XMPP.

Вот собственно и всё! Полный код примера можно получить с GitHub.

Для компиляции примера, вам необходимо установить gloox и pthreads. В Ubuntu, вы можете установить gloox используя sudo apt-get install libgloox-dev, а pthreads должен присутствовать в составе компилятора. Для компиляции с использование g++, введите команду g++ -o bot bot.cpp -lgloox -lpthread. Для других дистрибутивов Linux процесс должен быть аналогичен. В Windows, загрузите библиотеку с http://camaya.net/gloox/download и прочитайте инструкцию по установке.

Полный текст  bot.cpp

//A basic gloox tutorial by Anders Schau Knatten
//Read more at http://blog.knatten.org/2012/03/23/basic-gloox-tutorial/
//To compile on Linux: g++ -o bot bot.cpp -lgloox -lpthread
#include <iostream>
#include <string>
#include <gloox/client.h>
#include <gloox/message.h>
#include <gloox/messagehandler.h>
#include <gloox/connectionlistener.h>

using namespace std;
using namespace gloox;

ostream& operator<<(ostream& os, Message::MessageType type) {
    switch (type) {
        case Message::Chat:
            os << "Chat";
            break;
        case Message::Error:
            os << "Error";
            break;
        case Message::Groupchat:
            os << "Groupchat";
            break;
        case Message::Headline:
            os << "Headline";
            break;
        case Message::Normal:
            os << "Normal";
            break;
        case Message::Invalid:
            os << "Invalid";
            break;
        default:
            os << "unknown type";
            break;
    }
return os;
}

ostream& operator<<(ostream& os, const Message& stanza) {
    os << "type:'" << stanza.subtype() <<  "' from:'" << stanza.from().full() << "' body:'" << stanza.body() << "'";
    return os;
}

class ConnListener : public ConnectionListener {
public:
    virtual void onConnect() {
        cout << "ConnListener::onConnect()" << endl;
    }
    virtual void onDisconnect(ConnectionError e) {
        cout << "ConnListener::onDisconnect() " << e << endl;
    }
    virtual bool onTLSConnect(const CertInfo& info) {
        cout << "ConnListener::onTLSConnect()" << endl;
        return true;
    }
};

class Bot : public MessageHandler {
public:
    Bot() {
        JID jid("bot@localhost");
        client = new Client( jid, "botpwd" );
        connListener = new ConnListener();
        client->registerMessageHandler( this );
        client->registerConnectionListener(connListener);
        client->connect(true);
    }

    ~Bot() {
        delete client;
        delete connListener;
    }
 
    virtual void handleMessage( const Message& stanza, MessageSession* session = 0 ) {
        cout << "Received message: " << stanza << endl;
        Message msg(Message::Chat, stanza.from(), "Tell me more about " + stanza.body() );
        client->send( msg );
    }
 
private:
   Client* client;
   ConnListener* connListener;
};

int main() {
    Bot b;
}