Writing a command line XMPP bot (echobot) using Jaxl 2.0

In this blog post, we will write a sample XMPP bot (echobot) using Jaxl 2.0. In turn we will introduce ourselves to some of the basic functionality we can do using Jaxl e.g. fetching roster list, subscribing and unsubscribing to a user presence, etc. We will also focus on how to use XMPP extensions (XEP’s) inside our echobot code. Specifically, we will make use of XEP-0085 (chat state notification), XEP-0203 (delayed delivery) and XEP-0092 (software version) in our sample echobot application.

Echobot source code:
This sample echobot application comes packaged with Jaxl 2.0. If you have already read Installation, Usage guide and Running example apps, you might have even run this sample application.

Alternately, browse and download the sample source code from github echobot app files

Coding with Jaxl 2.0:
Every XMPP application developed using Jaxl 2.0, MUST have a jaxl.ini environment setup file inside your project directory. For example, packaged echobot sample application contains jaxl.ini which setup necessary Jaxl environment for our echobot application.

If you are developing an application from scratch, copy sample packaged jaxl.ini into your project folder:

cp /usr/share/php/jaxl/env/jaxl.ini /my/app/directory/jaxl.ini

and update JAXL_BASE_PATH and JAXL_APP_BASE_PATH with Jaxl installation path and your application directory path respectively.

Before we go on to write our echobot application code logic, lets see how an XMPP application code written using Jaxl 2.0 will generally look like:

<?php

        // Initialize Jaxl Library (#1)
        $jaxl = new JAXL();

        // Include required XEP's (#2)
        jaxl_require(array(
                'JAXL0085', // Chat State Notification
                'JAXL0092', // Software Version
                'JAXL0203'  // Delayed Delivery
        ), $jaxl);

        // Sample Echobot class (#3)
        class echobot {
                function startStream() {}
                function doAuth($mechanism) {}
                function postAuth() {}
                function getMessage($payloads) {}
                function getPresence($payloads) {}
        }
        $echobot = new echobot();

        // Add callbacks on various xmpp events (#4)
        JAXLPlugin::add('jaxl_post_connect', array($echobot, 'startStream'));
        JAXLPlugin::add('jaxl_get_auth_mech', array($echobot, 'doAuth'));
        JAXLPlugin::add('jaxl_post_auth', array($echobot, 'postAuth'));
        JAXLPlugin::add('jaxl_get_message', array($echobot, 'getMessage'));
        JAXLPlugin::add('jaxl_get_presence', array($echobot, 'getPresence'));

?>

Lets break down the above applications code structure (note the #1, #2, #3, #4 markers in the above code)

  • #1 – Create a new Jaxl instance inside your application. By doing so Jaxl core functionality and other utilities are made available inside your application code. You can also pass parameters while initializing Jaxl instance
    $jaxl = new JAXL(array(
            'user'    =>    'username',
            'pass'    =>    'password',
            'host'    =>    'talk.google.com',
            'domain' =>    'gmail.com',
            'port'    =>    5222
    ));

    Passed parameters will overwrite the values specified inside jaxl.ini file

  • #2 – Include required XMPP extensions (XEP’s) inside your application code. jaxl_require is a special method which makes sure that no Jaxl core class loads twice inside a running Jaxl instance.

    To include an implemented XEP inside your application code simply use:
    jaxl_require('JAXL0203', $jaxl);

    where 0203 is the XEP number for Delayed Delivery extension, which will help our echobot know if an incoming message is an offline message. If you wish to include more than one XEP inside your application code, simply pass the list of XEP’s as an array:

    jaxl_require(array(
    	'JAXL0085',
    	'JAXL0092',
    	'JAXL0203'
    ), $jaxl);

    where 0085 is xep number for Chat state notification extension and 0092 is the xep number for Software Version extension.

    Since version 2.1.0, jaxl_require MANDOTORY accepts Jaxl instance which require core classes, in this case $jaxl. It helps Jaxl core to keep a track of required core classes for every Jaxl instances in a multiple-instance application.

  • #3 – Now that we have Jaxl core and required XEP’s in our application environment, it’s time to write our application code logic. Application code MUST have methods which are called back by the Jaxl core when specific xmpp events occur during your application run time. Optionally, Jaxl core can pass parameters to these methods during callbacks.
  • #4 – In the final step, we will register callbacks for necessary xmpp events using JAXLPlugin class add() method inside our application logic. Syntax:
    JAXLPlugin::add($hook, $callback);

    In this example, we want to receive callbacks in our application code for following available hooks and events:

    1. jaxl_post_connect: After Jaxl has opened stream socket to the jabber server
    2. jaxl_get_auth_mech: When Jaxl have information about supported auth mechanisms by the jabber server
    3. jaxl_post_auth: After Jaxl finish authenticating the bot with the jabber server
    4. jaxl_get_message: When a new message stanza is received by Jaxl
    5. jaxl_get_presence: When a new presence stanza is received by Jaxl

Writing application logic:
Till now we have the registered callbacks for various xmpp events, it’s time to catch these callbacks inside our echobot class and implement the required functionality of our application. Below is an explanation for some of the important pieces inside echobot class:

  • startStream() method is called when jaxl_post_connect event occurs. For our application we will simply send XMPP start stream by calling $jaxl->startStream()
  • doAuth($mechanism) method is called when jaxl_get_auth_mech event occurs in Jaxl core. Jaxl core passes list of supported auth mechanism by the jabber server. Proceed with a preferred auth type by calling $jaxl->auth() method
  • postAuth() is called after Jaxl library has finished authenticating our application bot with the jabber server
  • getMessage($payloads) gets all new messages as they are received by Jaxl core
  • getPresence($payloads) gets all new presence as they are received by Jaxl core

Running Echobot:
Enter your application directory (/usr/share/php/jaxl/echobot). It MUST contain jaxl.ini, open and edit connecting echobot username, password and jabber host. Then from command line run echobot as follows:

root@ubuntu:/usr/share/pear/jaxl/app/echobot# jaxl echobot.php
== 5617 == [[2010-08-03 10:37:21]] Socket opened to the jabber host jaxl.im:5222 ...
== 5617 == [[2010-08-03 10:37:22]] Performing Auth type: DIGEST-MD5
== 5617 == [[2010-08-03 10:37:28]] Auth completed...
  • Pingback: Releasing Jaxl 2.0 – Object oriented XMPP framework in PHP | Abhi's Weblog

  • Pingback: Jaxl 2.0 – Installation, Usage guide and Example apps | Abhi's Weblog

  • Pingback: Tweets that mention Writing a command line XMPP bot (echobot) using Jaxl 2.0 | Abhi's Weblog -- Topsy.com

  • Arun

    Hi,
    I tried running the downloaded echobot in ubuntu 10.04 on PHP 5.3.2-1ubuntu4.2 with Suhosin-Patch (cli) (built: May 13 2010 20:01:00)

    Once I start the echobot, It just keep waiting after opening the socket successfully. something like

    PHP Deprecated: Comments starting with ‘#’ are deprecated in /etc/php5/cli/conf.d/mhash.ini on line 1 in Unknown on line 0
    PHP Warning: PHP Startup: Unable to load dynamic library ‘/usr/lib/php5/20090626+lfs/mhash.so’ – /usr/lib/php5/20090626+lfs/mhash.so: cannot open shared object file: No such file or directory in Unknown on line 0
    == 14323 == [[2010-08-08 22:06:37]] Socket opened to the jabber host talk.google.com:5223 …

    It keeps waiting. When I check the log in /var/log/jaxl.log It contains something like

    == 14027 == [[2010-08-08 21:30:36]] Jaxl stream not connected to jabber host, unable to send xmpp payload…

    == 14069 == [[2010-08-08 21:50:01]] [[XMPPSend]] 183

    == 14309 == [[2010-08-08 22:02:48]] [[XMPPSend]] 177

    == 14323 == [[2010-08-08 22:06:37]] [[XMPPSend]] 177

    Can you throw some light, what I’m missing out…

    Regards,
    Arun

  • http://abhinavsingh.com Abhinav Singh

    Hi Arun,

    Initial few error like PHP Deprecated and PHP Warning have to do with you PHP installation. Probably you have enabled mhash PHP extension but PHP is unable to find mhash.so.

    Also I can see you are trying to connect to port 5223, it should ideally be 5222. Kindly try out with port 5222 and lemme know.

  • Arun

    Hi Abhi,

    Thanks for your reply. Now that I corrected the port. But now I’m getting something like

    == 16233 == [[2010-08-09 08:01:39]] Socket opened to the jabber host talk.google.com:5222 …
    == 16233 == [[2010-08-09 08:01:42]] Performing Auth type: PLAIN
    PHP Warning: SimpleXMLElement::__construct(): Entity: line 1: parser error : Extra content at the end of the document in /usr/share/php/jaxl/core/jaxl.parser.php on line 126
    PHP Warning: SimpleXMLElement::__construct(): </stre in /usr/share/php/jaxl/core/jaxl.parser.php on line 126
    PHP Warning: SimpleXMLElement::__construct(): ^ in /usr/share/php/jaxl/core/jaxl.parser.php on line 126

    Help me out again…

  • http://abhinavsingh.com Abhinav Singh

    Hi Arun,

    This is probably because you have wrong credentials (username/password) combination inside jaxl.ini file.

    While connecting with google talk servers, if you provide wrong credentials, google talk servers respond back with something like $xml = “……</stream:stream>” which causes an invalid xml type detection.

    This shall be fixed in future releases, but as of now kindly make sure you have the right username password combination while connecting with gtalk servers.
    Lemme know how it goes.

  • Arun

    Hi Abihv,

    Thanks for your reply again. I’m sure the password is correct. But I tried with one more account ‘jaxl.test@gmail.com’. It works cool..!

    My original account is still not working. Any guess in this direction…

    Thanks, Regards,
    Arun

  • http://abhinavsingh.com Abhinav Singh

    I am not too sure. Try to connect to your original account using gmail integrated chat or desktop gtalk client, and then back again with Jaxl library. Hope this take us to a solution :)

  • Arun

    Hard luck again.. I’m able to login from my web interface… still no success with my original account… thanks a lot for your time that too in Monday morning.. :)

  • Arun

    Hi Abhinav,

    Got it… It’s expecting goolemail.com instead of gmail.com as a part of my original user id…!

    Regards,
    Arun

  • http://mypenguim.wordpress.com Daniel Santos

    Hi Abhinav,

    I was trying to make Jaxl to work on my CentOS 5.5 box, but for some reason it stucks after “Socket opened to the jabber host”… Has you mentioned above, I already checked my user/pass credentials, but nothing changes.
    My Jabber server is Openfire. Jaxl 1.0.2 is working fine up to now, using the same credencials… Do you have any idea of what are causing this?
    By the way, it would be interesting to have version dependencies like PHP minimal versions, which PHP libs must be installed, etc…

    By the way, GREAT WORK on Jaxl… really amazing job. Thanks a lot!

    • http://abhinavsingh.com Abhinav Singh

      Hi Daniel,

      Jaxl have following pre-requisites:
      - PHP5
      - fsockopen/curl/json support
      - OpenSSL enabled for TLS encryption

      Generally these dependencies are checked by Jaxl on startup and logs appropriate info inside jaxl.log. Jaxl library will undergo a lot of useful core changes from version 2.1.2 (currently in dev branch). Though core has been upgraded in a way that application codes have minimalistic changes to make (if at all). See CHANGELOG for list of changes in upcoming version.

      I personally haven’t runned Jaxl against an openfire server yet (but will do soon if you are unable to proceed further). To debug further can you tail your open fire server log to see what happens when Jaxl client tries to connect with it?

  • http://mypenguim.wordpress.com Daniel Santos

    Hey Abhinav,

    I just did some tests here… but nothing is logged on Openfire debug log… Jaxl just get stuck on “Socket opened to the jabber host” and nothing happens after.
    On jaxl.log I have this:

    Jaxl stream not connected to jabber host, unable to send xmpp payload…

    == 19265 == [[2010-11-18 10:20:34]] [[XMPPSend]] 183

  • http://mypenguim.wordpress.com Daniel Santos

    wordpress escaped… after the last line of previous comment we have this:

    ‘#/stream:stream#’

    You read # as

  • http://mypenguim.wordpress.com Daniel Santos

    Looks like I found the problem… CentOS 5.5 have PHP 5.1.6… When running from Ubuntu PHP 5.3.3 Jaxl works like a charm… So, I guess thats it.
    Thanks Abhinav.
    Regards.

  • http://abhinavsingh.com Abhinav Singh

    Hi Daniel,

    I have test Jaxl on following environment: PHP 5.2.10-2ubuntu6.4 with Suhosin-Patch 0.9.7 (cli). However I am sure Jaxl should would else where too after a few tweaks.

    Will it be possible for you to submit a patch if you can find out the fix. Else sometime later i will try to setup CentOS 5.5 with PHP 5.1.6.

  • http://mypenguim.wordpress.com Daniel Santos

    Yeap… I’ll make some tests and report to you asap.
    I guess that the main problem is on SSL support… lets see… I’ll keep you updated.
    Tks.

  • http://mypenguim.wordpress.com Daniel Santos

    Hi Abhinav,

    Today I was able to do some hacking and find out the problem… Maybe you could help… In xmpp.get.php, function proceed, the array['xmlns'] is null, so doesnt satisfy IF command… So I commented IF line and than it hungs again on function challenge cause of a null arr['xmlns'].
    Seems like the xml is not being correctly handled… I’ll try to find the object that’s causing the problem.

  • http://mypenguim.wordpress.com Daniel Santos

    Ok, now for sure I could assure that the problem is on JAXLXml function on jaxl.parser.php. For some reason its returnig a null array, and thats why I’m having problems on CentOS 5.5 PHP 5.1.6. Do you have any hints for me, so I can debug better where in parse function is the problem?
    Thanks in advance.

    • http://abhinavsingh.com Abhinav Singh

      Hi Daniel,

      Probably can be a bug inside JAXLXml::parse method which actually takes care of specified xml xpaths by various xep’s + core files. I will be in a better situation to assist you further if you can send me jaxl.log file and also the exact line inside xmpp.get.php where you face this problem….

      Also tonight i m going to do some experiments with Openfire + Jaxl and see how it goes, will keep you updated if i find anything.

  • http://mypenguim.wordpress.com Daniel Santos

    Hi Abhinav,

    The problem starts at line 125 (proceed function)on xmpp.get.php, where an if checks the array, but it is null, probably because its not being handled correctly by JAXLXml::parse as you told before.

    • http://abhinavsingh.com Abhinav Singh

      Hi Daniel,

      I am off on vacation till 25th dec. before i return back to my work place…. I have little access to internet or even a laptop to work with…. Will get back to this issue once i m back and will keep u updated….

    • http://abhinavsingh.com Abhinav Singh

      Hi Daniel, I finally did some tests with Openfire 3.6.4 + Windows XP box with latest version of Jaxl library downloaded from github. I can see everything going smooth with openfire. Will soon get the tests done against a CENT OS box and post my updates here.

  • http://abhinavsingh.com Abhinav Singh

    Hi Daniel, Finally i installed a CentOS image with PHP 5.1.6 and indeed Jaxl hangs after receiving <proceed xmlns=”…”> from the xmpp server. I am onto it and shall checkin the code with appropriate fix. To further track this issue i have opened a bug here: http://code.google.com/p/jaxl/issues/detail?id=25

  • http://abhinavsingh.com Abhinav Singh

    Hi Daniel, The issue has been fixed and pushed to master repo @github. Kindly test, verify the changes and let me know if the problem still persists. https://github.com/abhinavsingh/JAXL/commit/2978d2110526e08d76ce353f21742ccff5f4c1d5

  • http://mypeguim.wordpress.com Daniel Santos

    Thanks for your feedback Abhinav. If I can help in something else, please let me know.

  • http://mypeguim.wordpress.com Daniel Santos

    Ok, tested and fixed… Thanks A LOT!!! but I’m facing with these 3 warnings…

    PHP Notice: Undefined index: ping in /home/zabbix/jaxl2.1.2/xep/jaxl.0199.php on line 64

    PHP Notice: Undefined index: time in /home/zabbix/jaxl2.1.2/xep/jaxl.0202.php on line 67

    PHP Notice: Undefined index: offline in /home/zabbix/jaxl2.1.2/app/teste.php on line 68

  • http://abhinavsingh.com Abhinav Singh

    Hi Daniel,

    These warnings are probably happening because you must be using XEP 0199, 0202 inside your application.

    Not everytime they get a $payload['ping'] variable from JAXLXml parser and hence the PHP Notice. You can set appropriate error_reporting() level to ignore these.

  • http://mypeguim.wordpress.com Daniel Santos

    Great Abhinav… Thanks!! Keep the good work, Jaxl is really amazing.

  • Chetan

    Hello,
    Sir How can I pass arguments ?
    Like
    jaxl echobot.php username password
    I tried to use $argv[] but console returned this error
    “invalid number of parameters passed …”

    • http://abhinavsingh.com Abhinav Singh

      Hi Chetan,

      Kindly download and use v 2.1.2-rc1 which should remove this error for you.

  • Hafiz

    Hi Abhinav, already test your v2.1.2… Is this just can run on localhost? Can’t run on web hosting? I’m using PHP5 too and test your code more than 5 hours just now and nothing I got for possible run FB Chat… :(

    I think your guide not complete or not update. Hope you can make a new post and telling how to & step by step about new version…

    I already test your old version too, and seriously I got nothing here… And I don’t know this is the truth code can use FB Chat or not… Still try and try but now very stuck just focus on your code…

  • JRE

    Hi Abhinav, how can i do to know if one user has logged on from another client (talk, messengerfx, ebuddy etc) at the same time with the same account? it’s this possible?

    juanlopez -> gtalk
    juanlopez -> gmail (chat active)
    juanlopez -> an instance of JAXL

    I need to known if this user has a multiple chat active.

    Tanks for your help!! =)
    JAXL 2.1.2 works perfec!!!

  • Usman

    Can i know how to setup Jaxl with openfire

  • shaik.amesh

    Hi Abhinav,

    ofter executing the echobot.php i got the following error in jaxl.log file please help me

    [1:3560:0] 2012-07-17 13:32:31 – [[XMPP]]
    Socket opened to the jabber host talk.google.com:5222 …

    [1:3560:0] 2012-07-17 13:32:31 – [[XMPPSend]] 177

    [1:3560:0] 2012-07-17 13:32:31 – [[XMPPGet]] 4096

    [1:3560:0] 2012-07-17 13:32:31 – [[XMPPGet]] 4096
    X-GOOGLE-TOKENX-OAUTH2

    [1:3560:0] 2012-07-17 13:32:31 – [[XMPPGet]] OpenSSL extension required to proceed with TLS encryption

    • http://abhinavsingh.com Abhinav Singh

      https://github.com/abhinavsingh/JAXL from logs i can see you are still using Jaxl v2.x.
      Please start using v3.x is you don’t have any dependency upon the previous version.

  • shaik.amesh

    Hi Abhinav

    Thanks for fast reply

    As per mentioned i followed the above link and downloaded the v3.x, when i opened the example files in zend editor it showes the errors please help me with updated example files

    i got the error at the following code in some of the files in example folder

    $client->add_cb(‘on_auth_success’, function() {
    global $client;
    _debug(“got on_auth_success cb, jid “.$client->full_jid->to_string());
    $client->set_status(“available!”, “dnd”, 10);
    });

    • http://abhinavsingh.com Abhinav Singh

      you need to specify and give details of what error you really got

  • shaik.amesh

    Hi Abhinav,

    i got contact list from the account

    i got the contact list as below, please help me what’s error in first line and how to develop the chat list like as gmail

    OSType: Windows, Unable to register shutdown functions, try moving to a linux box OpenSSL: Enabled for CLI Starting TLS Encryption… Attempting PLAIN Authentication… Starting Session… Requesting Feature List… Requesting Roster List… Setting Status… Array ( [0] => shaik@gmail.com [1] => harta@gmail.com [2] => test@gmail.com ) Done

  • shaik.amesh

    Hi Abhinav,

    OSType: Windows, Unable to register shutdown functions, try moving to a linux box OpenSSL

  • shaik.amesh

    Hi Abhinav,

    ofter executing the script i got the following message in log file but i did not receive any message please help me

    2012-07-19 09:46:59
    Initializing class variables

    2012-07-19 09:46:59
    Trying to connect at talk.google.com:5222

    2012-07-19 09:46:59
    Connection made successfully at talk.google.com:5222

    2012-07-19 09:47:00
    Starting TLS Encryption…

    2012-07-19 09:47:02
    Already connected, sending start stream again at talk.google.com:5222

    2012-07-19 09:47:03
    Authenticating…

    2012-07-19 09:47:04
    Authentication Successful

    2012-07-19 09:47:05
    Binding to the server stream

  • shaik.amesh

    Hi Abhinav,

    i got the information as following but i did not receive any message, please help me

    1343042242 [INFO]: Connecting to tcp://talk.google.com:5222
    1343042243 [INFO]: Starting TLS encryption
    1343042243 [INFO]: Attempting Auth…
    1343042243 [INFO]: Auth success!
    1343042244 [INFO]: Bound to shaik.amesh@gmail.com/xmpphpA8781BCF
    1343042244 [INFO]: Session started
    Session Start
    Presence: shaik.amesh@gmail.com/Talk.v1044F691B34 [available]
    Presence: manda@gmail.com/gmail.993F350F [available]
    Presence: kishor@gmail.com/gmail.83603998 [available]
    Presence: raj@gmail.com/Talk.v104FB5AFB4C [available] Life isn’t a matter of milestones but of moments.
    Presence: venkat.varra@gmail.com/gmail.9D20AD8D [away]
    Presence: rajareddy511@gmail.com/Smack99CB5EFD [available]

  • http://abhinavsingh.com Abhinav Singh

    Announcing Jaxl v3.x – asynchronous, non-blocking I/O, event based PHP client/server library – http://abhinavsingh.com/blog/2012/07/announcing-jaxl-v3-x-asynchronous-non-blocking-io-event-based-php-clientserver-library/