📜 ⬆️ ⬇️

Chat bot applications via skype, jabber and whatsapp

Hello!
I would like to tell you the story of creating one uncomplicated entertainment service chat bots.

Introduction

About me: Habr has been reading for a long time (5 years old), but I registered a month ago, because I really wanted to share it. I work in an IT company as a testing engineer. But I was always drawn to the development. And 7 years ago I took up freelance in parallel with the main work. I started, like everything small, understood with the help of google. He wrote all kinds of web lewdness. All this in order to achieve certain financial goals. And in general, all life consisted of a cycle: searching for a goal — reaching by any means — satisfaction — looking for a new goal. So it was with freelance.

Crucial moment

It was the winter of 2012/2013. I worked on freelance on the project of an online consultant , and I had to do the integration with jabber and skype, that is, so that the operators could work with the client by typing in their im client, and the site user received it all on the web. After searching in google, it turned out that everything is simple with jabber, any xmpp server to choose from (I chose ejabbered) and the library to work with it JAXL (picked up on github), then version 2.x. But there were problems with Skype: the site in the Developers section offered some kind of kit for money (and a license prohibiting server use), with unnecessary activations for me and mentioning some kind of api. And I wanted something more simple. And then I stumbled upon this api in “Access to Skype API using PHP on * nix systems” , now on skype there is no mention of working with api already, although it was then, and I downloaded pdf to work with it via dbus. There was everything that I was looking for.

Several problems arose at once that were not described in the article I read, if the system is x64, then skype required some x86 dependencies:
')
sudo dpkg --add-architecture i386 sudo aptitude update 


Receiving messages on the server side:

 preg_match('#RECEIVED|SENT#Uis') 

- does not work correctly, as it responds to all messages
- and in general it is better not to use such a construction, since some messages disappear in the depths of dbus, and php does not get to them, that is, the script for the while () loop often does not have time to receive all messages or does not receive messages at all. By trial and error method was found 100% catching all the messages and even after idle script or reboot, here it is:

 $oSkype->Invoke('SEARCH MISSEDMESSAGES') 

Read more
 class phpSkype { private static $iLastId; public static function notify($sNotify) { //.... //.... //.... /* Message serving 4 */ if (preg_match('/^CHATMESSAGES/', $sNotify)) { $sNotify = str_replace('CHATMESSAGES ', '', $sNotify); if ($sNotify != '') { $aMessages = explode(', ', $sNotify); if (sizeof($aMessages)) { foreach ($aMessages as $iMessageId) { $sAuthRaw = $oSkype->Invoke('GET CHATMESSAGE ' . $iMessageId . ' FROM_HANDLE'); $sMessageRaw = $oSkype->Invoke('GET CHATMESSAGE ' . $iMessageId . ' BODY'); $aMessage = explode('BODY ', $sMessageRaw); $aUsernameSkype = explode('FROM_HANDLE ', $sAuthRaw); $aChatHash = explode('CHATNAME ', $oSkype->Invoke('GET CHATMESSAGE ' . $iMessageId . ' CHATNAME')); verifyUserSkypeChatId($aUsernameSkype[1], $aChatHash[1]); if (self::$iLastId < $iMessageId) { self::$iLastId = $iMessageId; addLog($aUsernameSkype[1], $aMessage[1]); self::received($aUsernameSkype[1], trim(strtolower($aMessage[1])), $iMessageId); } else { $oSkype->Invoke('SET CHATMESSAGE ' . $iMessageId . ' SEEN'); } } } } } //.... //.... //.... } public static function received($sUid, $sMessage, $iLastMsgId) { $oSkype = Zend_Registry::get('$oSkype'); // Mark received message as read $oSkype->Invoke('SET CHATMESSAGE ' . $iLastMsgId . ' SEEN'); /* Prepare */ $sChatIdRaw = $oSkype->Invoke('GET CHATMESSAGE ' . $iLastMsgId . ' CHATNAME'); $aChatId = explode('CHATNAME ', $sChatIdRaw); $sMembers = $oSkype->Invoke("GET CHAT " . $aChatId[1] . " ACTIVEMEMBERS"); $sMembers = str_replace('CHAT ' . $aChatId[1] . ' ACTIVEMEMBERS ', '', $sMembers); $aMembers = explode(' ', $sMembers); if (sizeof($aMembers) > 2) { $oSkype->Invoke("ALTER CHAT " . $aChatId[1] . " CLEARRECENTMESSAGES"); $oSkype->Invoke("ALTER CHAT " . $aChatId[1] . " DISBAND"); $oSkype->Invoke("ALTER CHAT " . $aChatId[1] . " LEAVE"); return false; } //.... //.... //.... } } $oDbus->registerObject('/com/Skype/Client', 'com.Skype.API.Client', 'phpSkype'); $bSMLastUpdate = 0; while (true) { $s = $oDbus->waitLoop(1); /* Get new skype messages */ global $bSMLastUpdate; if (time() - $bSMLastUpdate >= $aConfig['skype_get_new_message']) { $bSMLastUpdate = time(); $oSkype->Invoke('SET USERSTATUS ONLINE'); $oSkype->Invoke('SEARCH MISSEDMESSAGES'); } } 


Which sends all not processed messages to dbus (which do not have the SEEN flag):
 $oSkype->Invoke('SET CHATMESSAGE ' . $iMessageId . ' SEEN'); 

and in the main cycle we catch them with:
 preg_match('/^CHATMESSAGES/', $sNotify) 

The same is done for authorizations from users to the bot (the future user can register by simply sending the authorization on skype), namely:
 $oSkype->Invoke("SEARCH USERSWAITINGMYAUTHORIZATION") 

and not wait:
 preg_match('/^USER ([a-zA-Z0-9_\.]+) RECEIVEDAUTHREQUEST (.)+/', $sNotify, $aMatches) 

which also occasionally misfired. Another nuance is connected with this, if we need to know that the user has deleted the bot from the contact list, it is initiated:
 /* Client unlink */ if (preg_match('/BUDDYSTATUS 2/', $sNotify)) { //... } 

But you have to be careful and monitor the situation when a similar problem occurs when sending authorization from the bot to the user:
 $oSkype->Invoke("SET USER [USER_NAME] BUDDYSTATUS 2 [AUTH_MESSAGE]"); 

But I couldn’t solve the problem of skype bots going away after OS users crash after inactivity. Although in the skype settings there is away_mode = off and this line does not help:
 $oSkype->Invoke('SET USERSTATUS ONLINE'); 

Only after that, skype began to work stably.

And so I started setting up on ... freebsd then 9.x, like all my developments, oh, and I liked this system, I struggled with it for a long time, but I didn’t start skype through dbus, then I rested in session x- manager and dbus and their inconsistency and:
   /root/.dbus/session-bus/ id  dbus      export DBUS_SESSION_BUS_ADDRESS=8c9916eb531f5f9a0458961c000033bc-1 

it did not always help, I had to put everything on linux, as indicated in the article on Habré, I chose debian. Though backend web also remained on freebsd and they communicated among themselves through memcache. Yes, yes, a notable "bicycle", but more on that below.
The chat bots code was associated with the web code, I wanted to make it as soon as possible and therefore, instead of taking good decisions on Message Queues that I hadn’t used before, and didn’t understand much, I began to sculpt my turn from php + memcache with my logic lock'ov to make the registration, and the distribution of authorizations and messages in im clients of operators asynchronous. It was a terrible "machine" in which you could break your eyes - but it worked and still works, as far as I know. Imagine: 6 bots / OS users, 6 raised virtual desktops separately for each user debian, 6 vnc windows, each of them has 4 scripts (skype and jabber bot, the workers I wrote in line). Each server reboot turned into hard work, logged in with six users, open for each vnc, open a terminal and run 4 php scripts there. The bots routinely crashed with some strange errors, in particular JAXL, when using VNC dropped from:
 Interrupted system call  jaxl_loop.php 

it was treated by changing / deleting fields in the place of initialization of “new JAXL ()”, the search of which in google within the framework of very tight deadlines, did not allow to solve them carefully.

Ready, steady, go

In the spring of 2013, I decided to amuse my perfectionism and thought that I should write an interesting and necessary in terms of ideas, and beautiful in terms of code, and not as usual. And started looking for ideas. And she, as it turned out, was under my very nose. For example, I often call the calculator in windows7 or macos or watch the weather in my phone. And almost all day, while awake, I spend at the computer. The conclusion asked itself, it is necessary to transfer frequently used operations to constantly open applications and reduce the number of operations performed for existing tasks on the computer. Bingo. I frantically went to google to look for ready-made solutions, namely all kinds of applications implemented as chat bots through the popular im protocols. And at that time I did not find one (I was looking bad?). You need to write your own, it will be free and most importantly I will use it myself. I had to write a chat bots, which for USSD teams could interact with users via skype and jabber (later whatsapp), just write better than before and make a complete solution, choose a domain name, logo, site design, the site itself and everything the rest is necessary for this, that is, the full development cycle and deployment to prod.

The task is not easy, and I have never done it before. But one has only to do the 1st step ... And he was already done. To be honest, I immediately thought how cool it would be to monitor the server and have an ssh console emulation in my skype / jabber / whatsapp to monitor the server (while this task has been postponed for implementation, although there are some partial developments already).
And I really wanted to have an analogue of the “email for 10 minutes” service, having written the owner of a popular service about integration into mine, I did not expect anything good, my intuition did not let down, the developer of this system politely refused, saying that the service is free, and the developer barely covers his time spent advertising on this service. And I use it so often. Well, okay, I started to make my own bike on this topic, but I haven’t finished writing it yet, so I didn’t try to make all the tested ideas quickly.
It didn't work out with freelance (but I was not very upset, it was financially the goal, freelance helped solve it, there were no other goals, oh yes, I took myself a sport bike and now I’m sure there were no more goals) and free time appeared (although I continued to work constantly in the former IT company, but already on more interesting projects, but still so far from what I liked). For two months I sawed the code, anyway it was some kind of monster in terms of php code in the chat bot section. But the design of the site has so far remained almost untouched, I immediately started making flat ui - it seemed to me success in the near future. The end of spring, I experience personal problems of a different nature, and forget about this service and its development, I return in November 2013 and the first thing I do is delete all the chat bots code. And I start writing it again and without a queue for memcache.

From the scratch

I wrote the text of the future architecture, the structure of modules (web, api, bot). I compiled a documentation on the storage structure in memcache - in the future it all came to the rescue, so as not to keep large volumes of logic in his “memory palace”.
The beginning was in my head:
Web : freebsd 10 + php 5.x + apache + mysql 5.x + apc , in the second reading
php 5.x nginx + php-fpm, apc, opcache.
Chatbot : debian wheezy gnome + php 5.x + skype + memcache ( , ) + ejabbered JAXL 2.x ( 3.x)+ rabbitMQ ( ) PhpAmqpLib ( memcache github).

Architecture for each application: bot listner for skype, bot listner for jabber, bot listner for whatsapp, user worker (rabbitMQ) to process add / delete user and add / delete service, api worker (rabbitMQ) to call api methods, spamer worker (rabbitMQ) for sending messages and three helpers (for all protocols) are the simplest bot senders that receive commands from the user worker and the spamer worker, are also called in the background and are closed after work and immediately implement all bot activity ( withdraw the authorization, send the authorization with the message “X”, and send the message).

While I was sawing and testing, a colleague from work, who looked at my work, suggested:
And come on viber too!

And I again turned to my indispensable assistant - google. There was nothing about open api, but I found options for using whatsapp and screwed it to the service (I took the WhatsProt library code on github). But wait, because whatsapp is mainly for non-Russian speakers, you need to make translations for bots and on the site. I read “What languages ​​to translate the project in the first place?” And chose English, Russian, Simplified Chinese, German and Spanish, then refused to speak Chinese, no one in my acquaintances in it understood to make artistic changes.
Took ZF 2.x for the basis for the web. I planned to implement all the applications as a single bot, and then each application - as a separate bot. The first scheme was very unstable (since I still relied on memcache as an undocumented cache and as a “message queue”), and I refused it, but then returned partially. And the second one after 8 bots began to clutter up my contact list in skype and jabber, and also ate off resources on my local virtual machine at an incredible speed, especially skype itself, since for each bot I had to launch an individual user with my desktop and my skype ( although it is possible to run several different skype in the same environment, and bind to api each by id dbus sessions - but I have not tried it yet), and I abandoned this scheme, later I combined them, now each bot implements several applications grouped by Thoughts. I started with this set of applications:

For November and December and January I wrote an almost stable version and, most importantly, suitable for testing my friends.
But then the article “Google refuses to support XMPP” caught my eye, and I was a little upset. But after a while, after the article "Do not panic! About what Google did with XMPP, ” I realized that everything is just fine and I myself constantly use google xmpp. It's time to pour everything on the hosting. My test VM for 7 bots (three scripts for each bot / application (skype-jabber-whatsapp) and 3 workers for each application: user worker, api worker (I wrote all the interaction with the database through api - just why, so far I do not understand, but modularly and with one entry point for the web and bots it turned out, it became easier to test), and the spam worker, all workers worked through the Hare MQ) consumed 3.5 GB of RAM, and all hosters had tariffs of 2 and 4 GB with a cost of 2 times more for 4GB, which did not suit me, so all this is from my own pocket and the service is free, I wrote to the “digital ocean” (which I also read about l Habré) on the request of the needs of this service is to give the VM with a different number of RAM, but with a weaker processor, it was not critical. I refused. Expected. I found a comparison of hosters on “comparevps” and for money the same as for 2GB, I took the system with 4GB, although the actual experience with this hoster turned out to be slightly worse than expected, everything is worth its money (in this case, unfortunately). And I wrote two step-by-step instructions for installing freebsd and debian servers to the stage of a fully working service in order to quickly get purchased VPS.

But taking two servers (freebsd for web + debian for chat bots) was not easy, I started to put everything on one. And it was debian, I was unpleasantly surprised that I couldn’t put the latest versions of what I needed from packages, how could I do this on the freebsd build and ports. Then I figured out how I ended up being a linux and debian hater in particular, and became a fanatic in a good way (I now also have to have a Red Hat “must have”).

I added new applications to my friends on the tip:

Bots became 11, for each 6 scripts (1st – skype, 2nd –jabber, 3rd - whatsapp and three workers), the system lacked 4GB. The time has come to understand that it is necessary to combine applications into thematic bots, as I said earlier, it is said - done. Base, Office (calculator, currency rates, world clocks), Lifestyle (weather, horoscope, 8k), Developer (habra, hash) appeared. Now 2.5GB and all is well. Then I was a little tired, slowly read “Jabber switches to full encryption” and decided to test my server on xmpp.net and received an A- rating, which could not be good. By the way, for a long time I could not get my ejabbered server to become public (as well as add inter-server communication) until I did this in the DNS
image

But I still did not have ssl certificates for either the jabber or the site. Two for $ 60 I did not smile, and then I found the article "Get a free SSL certificate" and rushed. Everything is simple and clear.

There was always one problem that completely finished me: I was very tired of restoring all services with my hands, when the server because of the hoster fell or after a reboot just stood there, I had to solve somehow the problem that I only found out about it on Monday it happened on saturday. I tried many different things, in the end, the system after rebooting, completely automatically logs in all * nix users (for skype), the terminal starts under them in a graphical shell, opens six tabs and starts the necessary php script in each one so that I can go in and see it output at any time (for debug occasional minor issues):
Debian autologin all users and running scripts in the terminal in graphical mode
 >>nano /etc/inittab 8:2345:respawn:/bin/login -f _ tty8 </dev/tty8 >/dev/tty8 2>&1 9:2345:respawn:/bin/login -f lifestyle tty9 </dev/tty9 >/dev/tty9 2>&1 10:2345:respawn:/bin/login -f developer tty10 </dev/tty10 >/dev/tty10 2>&1 11:2345:respawn:/bin/login -f xmess tty11 </dev/tty11 >/dev/tty11 2>&1 12:2345:respawn:/bin/login -f office tty12 </dev/tty12 >/dev/tty12 2>&1 >>nano /etc/profile sleep 8 [ `tty` == '/dev/tty8' ] && startx -- :1 sleep 8 [ `tty` == '/dev/tty9' ] && startx -- :2 sleep 8 [ `tty` == '/dev/tty10' ] && startx -- :3 sleep 8 [ `tty` == '/dev/tty11' ] && startx -- :4 sleep 8 [ `tty` == '/dev/tty12' ] && startx -- :5 

“Sleep” is needed for everything to start, including skype.
And in the gnome autoload of each OS user (which is designed to execute bot scripts) add skype and bot maintenance scripts to:
 /home/USERNAME/.config/autostart/ 

 [Desktop Entry] Type=Application Exec=skype Hidden=false X-GNOME-Autostart-enabled=true Name[en_US]=skype Name=skype Comment[en_US]= Comment= 

 [Desktop Entry] Type=Application Exec=gnome-terminal --tab --title="skype" -e "/usr/local/bin/php /home/_/utils/assistant/_skype_assistant/base_assistant.php" --tab --title="jabber" -e "/usr/local/bin/php /home/_/utils/assistant/_jabber_assistant/base_assistant.php" --tab --title="whatsapp" -e "/usr/local/bin/php /home/_/utils/assistant/_whatsapp_assistant/base_assistant.php" --tab --title="user_op" -e "/usr/local/bin/php /home/_/utils/assistant/lib/worker/user_operations_worker.php base_assistant" --tab --title="api" -e "/usr/local/bin/php /home/_/utils/assistant/lib/worker/api_worker.php base_assistant" --tab --title="spamer" -e "/usr/local/bin/php /home/_/utils/assistant/lib/worker/spamer_worker.php base_assistant" Hidden=false X-GNOME-Autostart-enabled=true Name[en_US]=all_tabs Name=all_tabs Comment[en_US]= Comment= 


To reload the entire bot, I use:
 killall -u lifestyle   background  /bin/login -f lifestyle tty9 </dev/tty9 >/dev/tty9 2>&1 & 


And to get email when shutdown and / or startup used this script. Now I always knew what was happening with the server and knew that it would rise completely by itself and all the bots would work, even after surprises. Plus, I configured backup bd with rotation and storage of dumps into the mail (they are still small).

I read on Habré about heartbleed, and how, probably, I checked my server - everything was OK, but I couldn’t turn on TLS 1.2 and get F all the time, and I found exploit for heartbleed at the same time to test various services, because I never I didn’t study, it was interesting to try myself as a security engineer, I also tried nikto.pl and got inspired by nessus scanners. But back to the chat bots, I and libssl rearranged and openssl which “g” and checking the nginx dependencies, constantly saw that it was linked from the old lib, it turned out I just put nginx for squeeze instead of wheezy. Although this was the cause of the old libssl linked to nginx, due to which I did not have a vulnerability. “Accident,” you say. "Accidents are not accidental," I will answer.

By the way for whatsapp, as I understand it, the free receipt of the password from the account will be closed after a while, now it is very easy to get it:
get password for your whatsapp account
 /* Request sms code */ $username = ""; $token = md5($username); $nickname = "__"; $w = new WhatsProt($username, $token, $nickname, true); $w->codeRequest(); 

Then
 /* Get password */ $username = ""; $token = md5($username); echo $token; $nickname = "__"; $w = new WhatsProt($username, $token, $nickname, true); $result = $w->codeRegister("SMS___"); $password = $result->pw; echo "Password is $password"; 


But there is a problem that each new connection (in my scheme, one script listens to commands, and the other sends the answers received through the queue) interrupts the current one. It was necessary for whatsapp to refuse partly from the queues and to make an automatic restart of the php script itself.
Here is an interesting hint for the jabber bot, so that it shows the “typing” status when the user writes something to it:
JAXL bot typing status
 $oJabber->add_cb('on_chat_message', function($oStanza) { sMessage = trim(strtolower($oStanza->body)); if (!$sMessage) { $oStanza->to = $oStanza->from; $oStanza->from = $oJabber->full_jid->to_string(); $oJabber->send($oStanza); } //…. } 


, whatsapp skype , spamer , ,
 posix_kill(getmypid(), 9); 

jabber
 $oJabber->send_end_stream(); 

:

, . , , ( ) .

, - , github. , , xml whatsapp «typing» , :
whatsapp get user message
 $oWa->pollMessages(); $mData = $oWa->getMessages(); if (!empty($mData)) { if ('message' === $mData[count($mData) - 1]->getTag()) { if ('notify' === $mData[count($mData) - 1]->getChildren()[0]->getTag()) { $oDbAdapter->getConnection(); received($oWa->parseJID($mData[count($mData) - 1]->getAttributes()['from']), $mData[count($mData) - 1]->getChildren()[2]->getData());//$sUid, $sMessage $oDbAdapter->closeConnection(); } } } 


: «» , , ! .

PS 1 : , « : » . , . , . , , .

: Free Object Motion Tracking Plugin at Final Cut Pro X, Adobe Premier and DaVinci Resolve.

Source: https://habr.com/ru/post/220231/


All Articles