Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple sessions #666

Open
rdiasfreitas opened this issue Mar 2, 2015 · 34 comments
Open

Multiple sessions #666

rdiasfreitas opened this issue Mar 2, 2015 · 34 comments
Labels

Comments

@rdiasfreitas
Copy link

Hi there
Thanks for the beautiful work!

I'm working on a prototype that will need to have multiple sessions (different numbers) connected at the same time to whatsapp servers.

What I'm trying to do is:

  1. I have a class ThisIsMyLayer(YowInterfaceLayer), that connects to my backend services
  2. Stack declaration is implemented in a class that extends Threading, and I pass ThisIsMyLayer in the stack
  3. There's a main Thread, that starts N other threads, one to each number I wish to get connected

The problem is that it looks like Yowsup was not built to be used with multiple sessions, and when the second session is started, the solution stops working.

Could you please offer me some guidance in how to perform such a thing?

Thanks

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

@emamirazavi
Copy link
Contributor

You should find asyncore out correctly and use it to get the good result!
Just call asyncore.loop in new thread and don't use yowstack.loop directly and make new instance(yowstack) with new credentials again and again... no problem! several SIMs at the same time can be connected without any problem! But i think this asyncore is obsolete and i'm in doubt about using it by Tgalal!!!

@rdiasfreitas
Copy link
Author

Hi @emamirazavi

That's amazing, it's really working the way you suggested! Thanks a lot!

I come from a Java world, and I'm very familiar with multi-thread applications... But I'm just trying to figure it out how the Python world works. And I can tell you, it's not so easy to understand and start coding low level functions.

I've got this exception every time I restarted the application, I think it's related to some kind of synchronization, but I'm not sure how to start digging about that. Could you please help me?

asyncore.loop()

File "/usr/lib/python2.7/asyncore.py", line 216, in loop
poll_fun(timeout, map)
File "/usr/lib/python2.7/asyncore.py", line 156, in poll
read(obj)
File "/usr/lib/python2.7/asyncore.py", line 87, in read
obj.handle_error()
File "/usr/lib/python2.7/asyncore.py", line 83, in read
obj.handle_read_event()
File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
self.handle_read()
File "/storage/projetos/my_project/venv/lib/python2.7/site-packages/yowsup/layers/network/layer.py", line 47, in handle_read
data = self.recv(readSize)
File "/usr/lib/python2.7/asyncore.py", line 387, in recv
data = self.socket.recv(buffer_size)
error: [Errno 11] Resource temporarily unavailable

@emamirazavi
Copy link
Contributor

Probably this exception reads when you stop(terminate) your code. Does it?

On Tue, Mar 3, 2015 at 6:34 AM, Rodrigo Freitas notifications@github.com
wrote:

Hi @emamirazavi https://github.com/emamirazavi

That's amazing, it's really working the way you suggested! Thanks a lot!

I come from a Java world, and I'm very familiar with multi-thread
applications... But I'm just trying to figure it out how the Python world
works. And I can tell you, it's not so easy to understand and start coding
low level functions.

I've got this exception every time I restarted the application, I think
it's related to some kind of synchronization, but I'm not sure how to start
digging about that. Could you please help me?

asyncore.loop()

File "/usr/lib/python2.7/asyncore.py", line 216, in loop
poll_fun(timeout, map)
File "/usr/lib/python2.7/asyncore.py", line 156, in poll
read(obj)
File "/usr/lib/python2.7/asyncore.py", line 87, in read
obj.handle_error()
File "/usr/lib/python2.7/asyncore.py", line 83, in read
obj.handle_read_event()
File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
self.handle_read()
File
"/storage/projetos/my_project/venv/lib/python2.7/site-packages/yowsup/layers/network/layer.py",
line 47, in handle_read
data = self.recv(readSize)
File "/usr/lib/python2.7/asyncore.py", line 387, in recv
data = self.socket.recv(buffer_size)
error: [Errno 11] Resource temporarily unavailable


Reply to this email directly or view it on GitHub
#666 (comment).

@rdiasfreitas
Copy link
Author

No, it's just after the start...

@emamirazavi
Copy link
Contributor

Where do you use asyncore.loop?! You should use in a thread

import threading

loop_thread = threading.Thread(target=asyncore.loop, name="Asyncore
Loop")# If you want to make the thread a daemon# loop_thread.daemon =
True
loop_thread.start()

@see
http://stackoverflow.com/questions/14483195/how-to-handle-asyncore-within-a-class-in-python-without-blocking-anything

On Tue, Mar 3, 2015 at 7:06 AM, Rodrigo Freitas notifications@github.com
wrote:

No, it's just after the start...


Reply to this email directly or view it on GitHub
#666 (comment).

@emamirazavi
Copy link
Contributor

If in your view, everything is okay, what just stays suspicious is your
stack starter! See yowsup demos more accurate.
To start see EchoClient. Everything you want to start is there.

On Tue, Mar 3, 2015 at 7:10 AM, S.Mohammad Emami Razavi <
emamirazavi@gmail.com> wrote:

Where do you use asyncore.loop?! You should use in a thread

import threading

loop_thread = threading.Thread(target=asyncore.loop, name="Asyncore Loop")# If you want to make the thread a daemon# loop_thread.daemon = True
loop_thread.start()

@see
http://stackoverflow.com/questions/14483195/how-to-handle-asyncore-within-a-class-in-python-without-blocking-anything

On Tue, Mar 3, 2015 at 7:06 AM, Rodrigo Freitas notifications@github.com
wrote:

No, it's just after the start...


Reply to this email directly or view it on GitHub
#666 (comment).

@rdiasfreitas
Copy link
Author

Hmm, I did the changes you suggested, but the error is still happening.
I was spawning a new thread, and calling asyncore.loop inside the run(self), I think this would have the same behavior.
I will dedicate more time studying this issue, and I will keep you posted if I find something.

Thanks for the help

@tgalal tgalal added the feature label Apr 17, 2015
@rami-dabain
Copy link

@rdiasfreitas did it work out with you?

@yniv
Copy link

yniv commented Nov 12, 2015

@rdiasfreitas @emamirazavi does this work? I've implemented the stuff mentioned here but its unstable. each thread disconnects the other... any solution?

@yniv
Copy link

yniv commented Nov 16, 2015

it works! i moved all my multi-threaded code to asyncore and it is working now.

@eduardosan
Copy link

@yniv Can you give any specifics about your solution? I've tried the following solution:

def login(self):
        self.logger.debug("CLIENT: Iniciando login para %s", self.credentials)
        self.layer.init()
        self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
        try:
            self.stack.loop()
        except (KeyboardInterrupt, SystemExit):
            self.logger.error("CLIENT: Interrupcao forcada")
            return False
        except AuthError as e:
            self.logger.error("CLIENT: Erro de autenticacao!\n%s", e.message)
            # Desabilita o telefone
            self.disable_sender()
            return False
def call(self, method, params):
        return self.layer.call(method, params)

The I call login method in a thread:

t[sender.id] = threading.Thread(target=stack_list[sender.id].login)
t[sender.id].daemon = True
t[sender.id].start()

Calling the call method still producess the error:

stack_list[recipient.sender.id].call(
      'message_send',
       [recipient.get_full_phone(), campaign.message]
 )

This code still producess the error:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 763, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/vagrant/web/src/djyowsup/client/stack.py", line 61, in login
    self.stack.loop()
  File "/home/vagrant/.virtualenvs/plataforma_marketing_web/src/yowsup2/yowsup/stacks/yowstack.py", line 195, in loop
    asyncore.loop(*args, **kwargs)
  File "/usr/lib/python2.7/asyncore.py", line 216, in loop
    poll_fun(timeout, map)
  File "/usr/lib/python2.7/asyncore.py", line 156, in poll
    read(obj)
  File "/usr/lib/python2.7/asyncore.py", line 87, in read
    obj.handle_error()
  File "/usr/lib/python2.7/asyncore.py", line 83, in read
    obj.handle_read_event()
  File "/usr/lib/python2.7/asyncore.py", line 449, in handle_read_event
    self.handle_read()
  File "/home/vagrant/.virtualenvs/plataforma_marketing_web/src/yowsup2/yowsup/layers/network/layer.py", line 85, in handle_read
    data = self.recv(readSize)
  File "/usr/lib/python2.7/asyncore.py", line 387, in recv
    data = self.socket.recv(buffer_size)
error: [Errno 11] Resource temporarily unavailable

@fnbns
Copy link

fnbns commented Dec 12, 2015

Can you give more details regarding the loop in asyncore ? Thank you!

@yniv
Copy link

yniv commented Dec 17, 2015

@FunnyBones
the asyncore.loop() should be called only once in your server main().
you don't need to call stack.loop().
you don't need threads if you work with asyncore (only if you want an input thread or something like that)

hope that helps. ask me specific questions and i'll try to help more.

@fnbns
Copy link

fnbns commented Dec 17, 2015

@yniv thanks for the reply. I want to implement 2 numbers receiving messages in the same time. Unfortunately, I cannot come up with the flow for this. Can you guide me ?

@emamirazavi
Copy link
Contributor

Yowstack constructor does this. For each client you must call yowstack
separately. You must not call asyncore.loop after calling each yowstack and
it must be called once(see asyncore documentation).
On Dec 17, 2015 7:31 PM, "Alexandru Serban" notifications@github.com
wrote:

@yniv https://github.com/yniv thanks for the reply. I want to implement
2 numbers receiving messages in the same time. Unfortunately, I cannot come
up with the flow for this. Can you guide me ?


Reply to this email directly or view it on GitHub
#666 (comment).

@fnbns
Copy link

fnbns commented Dec 17, 2015

Can you post a small piece of c9de where you connect 2 clients ? Thabk you!

@jodersus
Copy link

How do you start more than one stack and NOT call asyncore.loop more than once?
Isn't starting one stack callilng asycore.loop?
Apologies if my question sounds too dumb.

@emamirazavi
Copy link
Contributor

Alex, please search for asyncore.loop in Yowsup. Finally it will be found
in the method. You must handle the method manually! I have no time to send
my code and after all this feature must not be developed by yowsup as
Tgalal mentioned me beforehand. It's not difficult and can make you more
sophisticated in Yowsup coding.

see this file:
https://github.com/tgalal/yowsup/blob/a59ac410c057e8042c528e9f73e9f3eaada6e79e/yowsup/stacks/yowstack.py

you must not call YowStack.loop and you should pay attention that asyncore
exists in Network layer. For each client finally you have a certain
NetworkLayer extending asyncore.dispatcher_with_send and YowLayer

see this file:
https://github.com/tgalal/yowsup/blob/92dcf0a54f663848e806411117d2f8bb1c0bda0b/yowsup/layers/network/layer.py

Give me a feedback.

On Sat, Dec 19, 2015 at 6:04 PM, Alex notifications@github.com wrote:

How do you start more than one stack and NOT call asyncore.loop more than
once?
Isn't starting one stack callilng asycore.loop?
Apologies if my question sounds too dumb.


Reply to this email directly or view it on GitHub
#666 (comment).

@johntheknife
Copy link

Hi, here is how I did it. I added the following lines on line 198 of yowstack.py

    def newLoop(self,isDaemon,onException,*args,**kwargs):
        if "discrete" in kwargs:
            discreteVal = kwargs["discrete"]
            del kwargs["discrete"]
            def discreteLoop(*args,**kwargs):
                while True:
                    asyncore.loop(*args, **kwargs)
                    time.sleep(discreteVal)
                    try:
                        callback = self.__class__.__detachedQueue.get(False) #doesn't block
                        callback()
                    except Queue.Empty:
                        pass
            t1=threading.Thread(target=discreteLoop,args=args,kwargs=kwargs)
        else:
            t1=threading.Thread(target=asyncore.loop,args=args,kwargs=kwargs)
        t1.daemon=isDaemon
        try:
            t1.start()
        except Exception as e:
            onException(e)
        except OSError as e:
            onException(e)
        return t1

It creates one thread for each loop. You can call it like this from the stack:

    def start(self):
        self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
        def onException(e):
            ...
        t1=self.stack.newLoop(isDaemon=True,onException=onException,timeout = 0.5, discrete = 0.5) #
        return t1

With this approach I have been able to create several threads, one for each line. But when I run disconnect() from the layer, asyncore fails with a 'Bad File Descriptor' error for one of the threads and all other threads disconnect.

@johntheknife
Copy link

And even then, I still get a [Errno 11] Resource temporarily unavailable, after a few minutes

@vickz84259
Copy link

I am planning to implement something similar. (Running different numbers on different threads)
From what I've understood about asyncore from different sites is that if you are planning to use asyncore on multiple threads, you should provide a global dictionary as the parameter, "map", to the dispatcher's constructor. Plus pass this dictionary to each loop you start in each thread.

I am planning to modify the dispatcher to implement this. I'll keep you guys updated if it works.

@emamirazavi
Copy link
Contributor

Victor, asyncore itself supports multiple sessions, it can handle over
almost 1k clients depends on your machine specifications, if the server
supports. Therefore using threading is very vain and incorrect method!
Asyncore uses eventlet to handle multiple connections. Yowsup uses
threading just to handle ping of connection.
On Dec 29, 2015 5:16 PM, "Victor Otieno" notifications@github.com wrote:

I am planning to implement something similar. (Running different numbers
on different threads)
From what I've understood about asyncore from different sites is that if
you are planning to use asyncore on multiple threads, you should provide a
global dictionary as the parameter, "map", to the dispatcher's constructor.
Plus pass this dictionary to each loop you start in each thread.

I am planning to modify the dispatcher to implement this. I'll keep you
guys updated if it works.


Reply to this email directly or view it on GitHub
#666 (comment).

@vickz84259
Copy link

I understand you. So in essence I can have three or more different stacks created under one thread and instead of calling the stack.loop of each stack, I call the asyncore.loop and they will work perfectly??

@emamirazavi
Copy link
Contributor

You can instantiate several stacks in single thread and don't call
stack.loop. But then you must call asyncore.loop once. All things will go
forward perfectly... asyncore.loop in one thread and all other instantiated
stacks in one other thread. It means generally you need just two threads.
After that you can trigger disconnect event to disconnect your client from
WA server or instantiate new stack to connect your desire client to WA
server freely without calling asyncore.loop again. Give me feedback.

On Wed, Dec 30, 2015 at 3:41 AM, Victor Otieno notifications@github.com
wrote:

I understand you. So in essence I can have three or more different stacks
created under one thread and instead of calling the stack.loop of each
stack, I call the asyncore.loop and they will work perfectly??


Reply to this email directly or view it on GitHub
#666 (comment).

@vickz84259
Copy link

Thank you so much for your feedback. Dude you are a life saver. Let me do just that and I will get back to you with the results.

@vickz84259
Copy link

This is what I have tried:

def stackInitiator():
        stack1 = YowStack(layers)
        stack2 = YowStack(layers)
        stack1.setCredentials(CREDENTIALS1)
        stack2.setCredentials(CREDENTIALS2)

        stack1.broadcastEvent(
            YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
        stack2.broadcastEvent(
            YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))

    thread = threading.Thread(target=stackInitiator)
    thread.start()

    asyncore.loop(timeout=1.0)

and here is the output:

DEBUG:yowsup.stacks.yowstack:Initializing stack
DEBUG:yowsup.stacks.yowstack:Constructed Network Layer
DEBUG:yowsup.stacks.yowstack:Constructed Stanza Regulator Layer
DEBUG:yowsup.stacks.yowstack:Constructed Crypt Layer
DEBUG:yowsup.stacks.yowstack:Constructed Coder Layer
DEBUG:yowsup.stacks.yowstack:Constructed Logger Layer
DEBUG:yowsup.stacks.yowstack:Constructed Axolotl Layer
DEBUG:yowsup.stacks.yowstack:Constructed Authentication Layer - Messages Layer - Receipt Layer - Iq Layer - Ack Layer
DEBUG:yowsup.stacks.yowstack:Constructed Interface Layer
DEBUG:yowsup.stacks.yowstack:Initializing stack
DEBUG:yowsup.stacks.yowstack:Constructed Network Layer
DEBUG:yowsup.stacks.yowstack:Constructed Stanza Regulator Layer
DEBUG:yowsup.stacks.yowstack:Constructed Crypt Layer
DEBUG:yowsup.stacks.yowstack:Constructed Coder Layer
DEBUG:yowsup.stacks.yowstack:Constructed Logger Layer
DEBUG:yowsup.stacks.yowstack:Constructed Axolotl Layer
DEBUG:yowsup.stacks.yowstack:Constructed Authentication Layer - Messages Layer - Receipt Layer - Iq Layer - Ack Layer
DEBUG:yowsup.stacks.yowstack:Constructed Interface Layer
DEBUG:yowsup.layers.network.layer:Connecting to e9.whatsapp.net:443
DEBUG:yowsup.layers.network.layer:Connecting to e9.whatsapp.net:443

No error...It just stops..

@emamirazavi
Copy link
Contributor

Victor, it's better you postpone some seconds to trigger connect event
after calling asyncore.loop(In fact you must trigger connect event after
onconnect event of asyncore being called) and to prevent thread to die
create a while loop at the end of stackInitiator method. Test it and give
the feedback.

On Thu, Dec 31, 2015 at 2:10 PM, Victor Otieno notifications@github.com
wrote:

This is what I have tried:

def stackInitiator():
stack1 = YowStack(layers)
stack2 = YowStack(layers)
stack1.setCredentials(CREDENTIALS1)
stack2.setCredentials(CREDENTIALS2)

    stack1.broadcastEvent(
        YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
    stack2.broadcastEvent(
        YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))

thread = threading.Thread(target=stackInitiator)
thread.start()

asyncore.loop(timeout=1.0)

and here is the output:

DEBUG:yowsup.stacks.yowstack:Initializing stack
DEBUG:yowsup.stacks.yowstack:Constructed Network Layer
DEBUG:yowsup.stacks.yowstack:Constructed Stanza Regulator Layer
DEBUG:yowsup.stacks.yowstack:Constructed Crypt Layer
DEBUG:yowsup.stacks.yowstack:Constructed Coder Layer
DEBUG:yowsup.stacks.yowstack:Constructed Logger Layer
DEBUG:yowsup.stacks.yowstack:Constructed Axolotl Layer
DEBUG:yowsup.stacks.yowstack:Constructed Authentication Layer - Messages Layer - Receipt Layer - Iq Layer - Ack Layer
DEBUG:yowsup.stacks.yowstack:Constructed Interface Layer
DEBUG:yowsup.stacks.yowstack:Initializing stack
DEBUG:yowsup.stacks.yowstack:Constructed Network Layer
DEBUG:yowsup.stacks.yowstack:Constructed Stanza Regulator Layer
DEBUG:yowsup.stacks.yowstack:Constructed Crypt Layer
DEBUG:yowsup.stacks.yowstack:Constructed Coder Layer
DEBUG:yowsup.stacks.yowstack:Constructed Logger Layer
DEBUG:yowsup.stacks.yowstack:Constructed Axolotl Layer
DEBUG:yowsup.stacks.yowstack:Constructed Authentication Layer - Messages Layer - Receipt Layer - Iq Layer - Ack Layer
DEBUG:yowsup.stacks.yowstack:Constructed Interface Layer
DEBUG:yowsup.layers.network.layer:Connecting to e9.whatsapp.net:443
DEBUG:yowsup.layers.network.layer:Connecting to e9.whatsapp.net:443

No error...It just stops..


Reply to this email directly or view it on GitHub
#666 (comment).

@vickz84259
Copy link

Ok.. Updating it with your recommendations.

@rami-dabain
Copy link

problem is how to add layers after the core.loop?

@johntheknife
Copy link

Hi, I have created an http interface project that uses telegram bot api to communicate through whatsapp using yowsup. You can check it out here.

Unfortunately, I am having big trouble in connecting with several accounts simultaneously. In fact, I have resorted to using different docker containers, one for each account. I wonder if you can check it out and let me know how to make several accounts run in the same container.

@bernardogontijo
Copy link

I got it working with a very simple solution:

class Phones(object):
    phs=[]
    def __init__(self,cred):
        self.cred=cred
        self.__class__.phs.append(self)
        stackBuilder = YowStackBuilder()
        self.stack = stackBuilder\
            .pushDefaultLayers(True)\
            .push(EchoLayer)\
            .build()
        print("started for "+cred[0])
        self.stack.setProp(YowAuthenticationProtocolLayer.PROP_CREDENTIALS, self.cred)       
        self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))          
        self.stack.setProp(YowNetworkLayer.PROP_ENDPOINT, YowConstants.ENDPOINTS[0])           
        self.stack.setProp(YowCoderLayer.PROP_DOMAIN, YowConstants.DOMAIN)
        self.stack.setProp(YowCoderLayer.PROP_RESOURCE, YowsupEnv.getCurrent().getResource())
    @classmethod
    def start(cls):
        asyncore.loop()


    @classmethod
    def send(cls,data):
        for c in cls.phs:
            if c.cred[0]+'@s.whatsapp.net'==data["phone_from"]:
                c.stack.broadcastEvent(YowLayerEvent('send_message', message=data['message'], phone_to=data['phone_to']))



It is very easy to use:




CREDENTIALS=[('phone','pass')]
for cred in CREDENTIALS:
    Phones(cred)
Phones.start()



@maxi7587
Copy link

maxi7587 commented Jun 6, 2019

I have tried a similar solution, but the code after self.stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT)) is never executed...

@maxi7587
Copy link

maxi7587 commented Jun 6, 2019

I could make it work for multiple lines, but had to comment out line 33 in dispatcher_asyncore.py:

asyncore.loop(timeout=1)

This line was blocking the code, preventing other lines to connect to Whatsapp.

@Jean-Pier
Copy link

buenas, intento hacer un bot de WASAP con varias sesiones(diferentes numeros) pero al 2 numero, le responde lo que el 1 numero tiene q ver, porfavor alguien sabe como lo puedo solucionar para diferentes numeros?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests