joaoricardo000 / whatsapp-bot-seed

A small python framework to create a whatsapp bot, with regex-callback message routing.
728 stars 244 forks source link

How to add a trigger to an external event and send a msg #23

Closed joyarzun closed 8 years ago

joyarzun commented 8 years ago

Hi there. I want to add an external event and this trigger a message by WA. The external event is other python process that has motion detection. So, when it detects motion this should trigger a msg to whatsapp-bot. In your project, the event is a message received that trigger a response. In a wider context, I have a raspberry pi with a cam. I want that RPI send me a message if the cam detect motion. How I can accomplish this? Any advice or help are grateful

x23piracy commented 8 years ago

Hi,

yes it would be really nice to have the possibility to trigger a python script or something while the bot is running to tell him send this or that to some contact.

Is this already possible?

Regards X23

joaoricardo000 commented 8 years ago

Hi!

To achieve this I used messaging with RabbitMQ. I created a new layer that listened to a queue that receives stomp messages with the phone number and message as parameters, and just send it.

That is the Layer code:

import threading
import pika
import config
import simplejson
from yowsup.layers.interface import YowInterfaceLayer
from yowsup.layers.protocol_messages.protocolentities.message_text import TextMessageProtocolEntity

class RabbitMQLayer(YowInterfaceLayer):
    def __init__(self):
        super(RabbitMQLayer, self).__init__()
        self.connection = pika.BlockingConnection(pika.ConnectionParameters(config.rabbitmq["host"]))
        self.stomp_channel_service = self.connection.channel()
        self.queue_name = "queue/whatsapp/message/send/request"
        threading.Thread(target=self.listen_queue, args=()).start()

    def on_message_request(self, ch, method, properties, body):
        ch.basic_ack(delivery_tag=method.delivery_tag)
        message = simplejson.loads(body)
        self.toLower(TextMessageProtocolEntity(message["text"].encode("UTF-8"), to=message["to"]))

    def listen_queue(self):
        self.stomp_channel_service.queue_declare(queue=self.queue_name, durable=True)
        self.stomp_channel_service.basic_consume(self.on_message_request, queue=self.queue_name)
        self.stomp_channel_service.start_consuming()

And just add the layer on server.py

stack_builder.push(YowParallelLayer([RouteLayer, NotificationsLayer, RabbitMQLayer]))

With this layer working on the bot, any message sent to this queue with "body" and "to", will send a whatsapp message.

A simple python script that sends a stomp message looks like this:

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel_service = connection.channel()
channel_service.basic_publish(
            exchange='',
            routing_key="queue/whatsapp/message/send/request",
            body=simplejson.dumps({"to":"554899998888", "text":"Test Message!"})

I made this as a test couple months ago, but I think it still works. If this code looks strange to you, please check some examples on pub/sub with RabbitMQ first. Any problems, just ask!

x23piracy commented 8 years ago

Hi,

i get: ImportError: No module named rabbitmq.rabbitmq_layer Is there maybe something missing?

under layers dir i created rabbitmq dir within a file called rabbitmq_layer.py with your code the i added in router.py: from layers.rabbitmq.rabbitmq_layer import RabbitMQ

but i get the error above: ImportError: No module named rabbitmq.rabbitmq_layer

Edit:

i think i got one step closer, i created the rabbitmq_layer.py under /layers/notifications but when i now start the bot i get:

Traceback (most recent call last): File "/opt/whatsapp-bot-seed-master/src/server.py", line 53, in server = YowsupEchoStack(config.auth) File "/opt/whatsapp-bot-seed-master/src/server.py", line 28, in init stack_builder.push(YowParallelLayer([RouteLayer, NotificationsLayer, RabbitMQLayer])) File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/init.py", line 147, in init self.sublayers = tuple([sublayer() for sublayer in sublayers]) File "/opt/whatsapp-bot-seed-master/src/layers/notifications/rabbitmq_layer.py", line 12, in init self.connection = pika.BlockingConnection(pika.ConnectionParameters(config.rabbitmq["host"])) AttributeError: 'module' object has no attribute 'rabbitmq'

Can you please help me get this running? :)

Regards X23

x23piracy commented 8 years ago

Hi,

please help me with this, i have problems to get this running.

Regards X23

ghost commented 8 years ago

@x23piracy Try installing the module with sudo pip install pika

x23piracy commented 8 years ago

Hi,

pika has been already installed. Is my integration correct? See my post above, i think maybe some steps missing in joaoricardo000 post? Can someone try this also please?

The error is: AttributeError: 'module' object has no attribute 'rabbitmq' I don't know if my steps additionally to joaoricardo000's are correctly done.

Regards X23

ghost commented 8 years ago

In the layer script you're having an error in self.connection = pika.BlockingConnection(pika.ConnectionParameters(config.rabbitmq["localhost"])). In the joaoricardo000's post, that line is a bit different self.connection = pika.BlockingConnection(pika.ConnectionParameters(config.rabbitmq["host"]))

x23piracy commented 8 years ago

Hi,

sorry for the confusion while i was trying making it run i thougth this is the hostname rabbitmq connects to, therefore i changed to localhost for a test away from host but in both cases error :) or :(

@joaoricardo000 can you clear this please?

and i get the error:

Traceback (most recent call last): File "/opt/whatsapp-bot-seed-master/src/server.py", line 53, in server = YowsupEchoStack(config.auth) File "/opt/whatsapp-bot-seed-master/src/server.py", line 28, in init stack_builder.push(YowParallelLayer([RouteLayer, NotificationsLayer, RabbitMQ]))

NameError: global name 'RabbitMQ' is not defined

What i am missing?

Just for fun i added to server.py also: from layers.notifications.rabbitmq_layer import RabbitMQ and/or from layers.notifications.rabbitmq_layer import RabbitMQLayer

but anyway:

Traceback (most recent call last): File "/opt/whatsapp-bot-seed-master/src/server.py", line 53, in server = YowsupEchoStack(config.auth) File "/opt/whatsapp-bot-seed-master/src/server.py", line 28, in init stack_builder.push(YowParallelLayer([RouteLayer, NotificationsLayer, RabbitMQ])) NameError: global name 'RabbitMQ' is not defined root@NbI-1527:/opt/whatsapp-bot-seed-master/src# python /opt/whatsapp-bot-seed-master/src/server.py Traceback (most recent call last): File "/opt/whatsapp-bot-seed-master/src/server.py", line 16, in from layers.notifications.rabbitmq_layer import RabbitMQ

ImportError: cannot import name RabbitMQ

Edit:

Ok one step closer:

Traceback (most recent call last): File "/opt/whatsapp-bot-seed-master/src/server.py", line 53, in server = YowsupEchoStack(config.auth) File "/opt/whatsapp-bot-seed-master/src/server.py", line 28, in init stack_builder.push(YowParallelLayer([RouteLayer, NotificationsLayer, RabbitMQLayer])) File "/usr/local/lib/python2.7/dist-packages/yowsup/layers/init.py", line 147, in init self.sublayers = tuple([sublayer() for sublayer in sublayers]) File "/opt/whatsapp-bot-seed-master/src/layers/notifications/rabbitmq_layer.py", line 12, in init self.connection = pika.BlockingConnection(pika.ConnectionParameters(config.rabbitmq["host"]))

AttributeError: 'module' object has no attribute 'rabbitmq'

Line 12 is: self.connection = pika.BlockingConnection(pika.ConnectionParameters(config.rabbitmq["host"]))

That was the reason i was thinking host is the hostname of the machine where rabbitmq is running on so i tried localhost but still nogo.

was apt-get install rabbitmq-server correct? Is this error caused maybe because rabbitmq is not running? How can i check this?

Regards X23

joaoricardo000 commented 8 years ago

Almost there...

self.connection = pika.BlockingConnection(pika.ConnectionParameters(config.rabbitmq["host"]))

Either add on config.py a new line for rabbitmq config, like this:

rabbitmq = {"host":"localhost"}

or just replace it

self.connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))

Good luck!

x23piracy commented 8 years ago

Hi,

Like i said also if i change this i get error, please see my posts above.

Could you give me a step by step howto integrate this solution please.

Regards X23

Am 28.01.2016 um 12:56 schrieb João Ricardo notifications@github.com:

Almost there...

self.connection = pika.BlockingConnection(pika.ConnectionParameters(config.rabbitmq["host"]))

Either add on config.py a new libe for rabbitmq config, like this:

rabbitmq = {"host":"localhost"}

or just replace it

self.connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))

Good luck!

— Reply to this email directly or view it on GitHub.

joaoricardo000 commented 8 years ago

Yes, I read your posts...

AttributeError: 'module' object has no attribute 'rabbitmq'

This was the last error you complained about, and that still the solution. About problems with RabbitMQ, I strongly advise you to practice and study a little bit more on examples about how to listen and receive messages, on simple test scripts. There is a bunch of tutorials about RabbitMQ + python, and while you don't feel comfortable with it, integrating this on the bot will not be simple...

Good luck..

x23piracy commented 8 years ago

Hi,

ok i have the bot now starting up with no errors. When i was rude, i was a little bit blind because you already gave me the solution. Sorry for this.

Well since the bot starts no without error i created your example script to send a test message, changed the number to my cell phone number and executed the script:

root@NbI-1527:/tmp# python test.py Traceback (most recent call last): File "test.py", line 9, in <module> body="Test Message!") TypeError: basic_publish() got an unexpected keyword argument 'to'

queue is there while bot running. root@NbI-1527:/tmp# sudo rabbitmqctl list_queues Listing queues ... queue/whatsapp/message/send/request 0 ...done.

Is a code change needed? Which version of pika was used with your code?

Edit:

How can i define the keyword 'to' to work? I read something about headers? @joaoricardo000 could you please take a look?

Regards X23

x23piracy commented 8 years ago

Hi,

i've got rabbitmq working but for your send example i had to remove 'to' and hardcoded recipient within server.py. Now i can send messages but i have to figure out how can i embed the 'to' if not i will abuse the body for message and recipient and split the string before processing.

@joaoricardo000 If you want you can close now.

Regards X23

joyarzun commented 8 years ago

Hi there Now I make me sometime to read the examples and doc for RabbitMQ. Finally I figure out that is too complicate for my needs. It required to run a server of rabbit only for messaging. I expect something more simple, for example, communication between process with multiprocessing library as explain in http://stackoverflow.com/a/6921402 In simple, you send a msg and other process is waiting for him. My problem is how to the waiter process is mix with stack or layer of yosuw. I supposes, your example make a thread that wait for msg and don't interrupt the stack.start() cycle. Then when arrive a msg, the thread call a function in the cycle's stack that trigger a wzp msg, and all happy. Well my english sucks as usual.

Regards

joaoricardo000 commented 8 years ago

@x23piracy Hi, I updated my example above. Just send a json as string on the body message, and when received, parse json to dict and problem solved!

@joyarzun Yes, communicating between process works ok, and you don't have to install nothing. But messaging is so much usefull! Think about the possibilities! You can have 100 bots running, and all of them subscribing the same queue. Other processes in different machines (and languages) could send a request to it with no problem! And yeah, probably overkill to what you asked haha But glad you did what you wanted!

x23piracy commented 8 years ago

@joaoricardo000 Thank you for the code change (you are missing the closing ")" and import simplejson). I Changed it and gave it a try but no luck but also no error...strange.

When starting the bot: _blocking_connection.py_ [INFO][2016-02-05 01:35:38,684] Created channel=1

Then when i execute the example: it just executes the script no error but also no message via whatsapp.

Is only the international number needed or is it needed to attach @s.whatsapp.net?

Regards X23