Este sitio utiliza cookies propias y de terceros. Si continúa navegando consideramos que acepta el uso de cookies. OK Más Información.

Bot Whatsapp en Python

  • 11 Respuestas
  • 20289 Vistas

0 Usuarios y 7 Visitantes están viendo este tema.

Desconectado ANTRAX

  • *
  • Administrator
  • Mensajes: 5490
  • Actividad:
    6.67%
  • Reputación 36
  • ANTRAX
    • Ver Perfil
    • Underc0de
    • Email
  • Skype: underc0de.org
  • Twitter: @Underc0de
« en: Febrero 22, 2017, 02:01:39 pm »

Buenas, quería compartir un proyecto que tengo hace un tiempo con respecto a whatsapp, últimamente se esta poniendo muy difícil debido a los bloqueos, las actualizaciones que saca whatsapp para combatir esto, ademas que no tiene una api, las que se crearon o existen de forma no oficial están teniendo problemas.

Ya hace algún tiempo en github salieron apis para whatsapp, una de las primeras que use fue whatsapi después llamada chatapi, esta en php, si no me equivoco es la primera que salio, en base a esta se creo su equivalente en python llamada yowsup.

Para utilizar esta api primero hay que instalarla, después hacemos uso de sus clases en python.

El código que voy a compartir es básico, responde de acuerdo a algunas palabras claves, también cuenta con algunos scrapers de paginas, una de ellas es el inpres, también cuenta con un monitor de inpres que tiene definido ciertos parámetros para enviar alertas,en el nuevo trabajo que estoy haciendo estoy tratando de implementar chatterbot como motor de respuestas.

Empecemos, primero instalar la api.

Código: Bash
  1. git clone https://github.com/tgalal/yowsup.git

ir a la carpeta env y editar el archivo env_s40.py en github todavía no esta actualizado, no se por que pero no tiene los valores correctos y jode por el token, por lo que si no lo editan no podrán utilizar la api, error.

Aquí están los tokens correctos

https://coderus.openrepos.net/whitesoft/whatsapp_scratch

Editar en ese archivo las variables

Código: Text
  1. _VERSION = “2.16.9”
  2.  
  3. _TOKEN_STRING = “PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1471306425030{phone}”

Por

Código: Text
  1. _VERSION = “2.16.11”
  2.  
  3. _TOKEN_STRING = “PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1478194306452{phone}”

Y con esto debería funcionar, ahora falta compilar de nuevo.

Código: Bash
  1. sudo python setup.py build

Instalar

Código: Bash
  1. sudo python setup.py install

Recomiendo comprar un chip descartable o prepago para registrar en whatsapp y utilizar la api, por los bloqueos, no queremos perder nuestra linea en whatsapp.

Procedemos a registrar nuestra linea, todo eso desde yowsup cli 2.0, el objetivo es registrar y obtener la clave que esta codificada en base64

Solicitamos el OTP

Código: Bash
  1. yowsup-cli registration --requestcode sms --phone 549XXXXXXXXXX --cc 54 --mcc 722 --mnc XX

mmc y mnc por pais

https://es.wikipedia.org/wiki/MCC/MNC

Ejemplo

Código: Bash
  1. yowsup-cli registration --requestcode sms --phone 5493804651130 --cc 54 --mcc 722 --mnc 341

Debería salir en status : sent
mmc 722 corresponde a ARG
mnc 341 corresponde a PERSONAL

Antes de solicitar la OTP ponemos el chip en cualquier celular, solo nos interesa el sms con el código de activación
Una vez que nos llego

Código: Bash
  1. yowsup-cli registration --register XXXXX --phone 549XXXXXXXXXX --cc 54 -d

Ejemplo:

Código: Bash
  1. yowsup-cli registration --register 542301 --phone 5493804651130 --cc 54 -d

Si todo sale bien nos devolverá la clave.
ahora creamos un archivo con la configuración o credenciales.

Código: Bash
  1. nano 5493804651130.cfg

Dentro del mismo

cc=54
phone=549XXXXXXX
id=549XXXXXXX
password=b64pw

Ejemplo

cc=54
phone=5493804651130
id=5493804651130
password=m+S232dtFUfghljjKS12325pZmNELasdas=

Probamos todo en el cli.

Código: Bash
  1. yowsup-cli demos -c 5493804651130.cfg -e

-c = archivo de configuración creado anteriormente
-e = modo eco.

El modo eco, responde a todo lo que le enviamos.

Ahora probamos enviar un whatsapp que diga hola mundo a ese numero debería responder con hola mundo.

Si todo sale bien, estamos listos para utilizar el script.

DESCARGAR CODIGO: http://elfsystems.pro/files/MOT_IMPRES-WA.zip

Editar el array CREDENTIALS con sus datos en run.py

Código: Python
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. from yowsup.stacks                             import YowStackBuilder
  4. from yowsup.common                             import YowConstants
  5. from yowsup.layers                             import YowLayerEvent
  6. from layer                                     import EchoLayer
  7. from yowsup.layers.auth                        import YowAuthenticationProtocolLayer
  8. from yowsup.layers.coder                       import YowCoderLayer
  9. from yowsup.layers.network                     import YowNetworkLayer
  10. from yowsup.env                                import YowsupEnv
  11. from scrapers.inpres                           import SCinpres
  12. import threading, os, subprocess, time, sys
  13.  
  14. CREDENTIALS = ("549XXXXXXXXX", "pwB64")
  15. laye = EchoLayer()
  16.  
  17. def workerinpress():
  18.         inpre = SCinpres()
  19.         print "Monitor inpres start\n"
  20.         while (True):
  21.                 al = inpre.monitor()
  22.                 if (al and al.strip() and al !='Error en consulta'):
  23.                         print al
  24.                         laye.envialertainpres(al)
  25.                 time.sleep(1)
  26.  
  27. def workerwhatsapp():
  28.         stackBuilder = YowStackBuilder()
  29.         stack = stackBuilder.pushDefaultLayers(True).push(laye).build()
  30.         stack.setCredentials(CREDENTIALS)
  31.         stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT))
  32.         stack.setProp(YowNetworkLayer.PROP_ENDPOINT, YowConstants.ENDPOINTS[0])
  33.         stack.setProp(YowCoderLayer.PROP_DOMAIN, YowConstants.DOMAIN)
  34.         stack.setProp(YowCoderLayer.PROP_RESOURCE, YowsupEnv.getCurrent().getResource())
  35.         stack.loop( timeout = 0.5, discrete = 0.5 )
  36.  
  37. if __name__==  "__main__":
  38.         threads = list()
  39.         moninp = threading.Thread(target=workerinpress)
  40.         threads.append(moninp)
  41.         wasa = threading.Thread(target=workerwhatsapp)
  42.         threads.append(wasa)
  43.         moninp.start()
  44.         wasa.start()

En layer.py editar

permitidos=[‘5493834621141′,’5493834654414’]   #Agregar los números a los cuales solo quieren responder

name = “Bort” #nombre del usuario
ALERTAN = (“[email protected]”)  #numero al cual queremos enviar las alertas del impres

Código: Python
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. import os, subprocess, time, sys
  4. from sys import exit
  5. from yowsup.layers.interface                           import YowInterfaceLayer,ProtocolEntityCallback    
  6. import mimetypes
  7. from yowsup.layers.protocol_messages.protocolentities  import TextMessageProtocolEntity  
  8. from yowsup.layers.protocol_presence.protocolentities  import AvailablePresenceProtocolEntity  
  9. from yowsup.layers.protocol_presence.protocolentities  import UnavailablePresenceProtocolEntity
  10. from yowsup.layers.protocol_presence.protocolentities  import PresenceProtocolEntity            
  11. from yowsup.layers.protocol_chatstate.protocolentities import OutgoingChatstateProtocolEntity  
  12. from yowsup.common.tools                               import Jid
  13. from yowsup.layers.protocol_media.mediadownloader      import MediaDownloader
  14. from yowsup.layers.protocol_media.protocolentities     import *
  15. from respuesta                                             import Respuesta                              
  16. import sys, shutil
  17.  
  18. permitidos=['5493834621141']
  19. ac = set(permitidos)
  20.  
  21. name = "Bort"
  22. ALERTAN = ("[email protected]")
  23.  
  24. class EchoLayer(YowInterfaceLayer):
  25.  
  26.         @ProtocolEntityCallback("message")
  27.         def envialertainpres(self, al):
  28.                 self.toLower(AvailablePresenceProtocolEntity())
  29.                 time.sleep(0.5)
  30.                 self.toLower(PresenceProtocolEntity(name = name))#Set presencia
  31.                 time.sleep(0.5)
  32.                 textmsg = TextMessageProtocolEntity
  33.                 for tel in ALERTAN:
  34.                         self.toLower(textmsg(al, to = tel))
  35.                         time.sleep(0.4)
  36.                 time.sleep(1)
  37.                 self.toLower(UnavailablePresenceProtocolEntity())#Setoffline
  38.  
  39.         @ProtocolEntityCallback("message")
  40.         def onMessage(self, messageProtocolEntity):
  41.                 if messageProtocolEntity.getType() == 'text':
  42.                         time.sleep(0.5)
  43.                         self.toLower(messageProtocolEntity.ack())#Set visto
  44.                         time.sleep(0.5)
  45.                         self.toLower(AvailablePresenceProtocolEntity())
  46.                         time.sleep(0.5)
  47.                         self.toLower(PresenceProtocolEntity(name = name))#Set presencia
  48.                         time.sleep(0.5)
  49.                         self.toLower(messageProtocolEntity.ack(True))#Set doble visto
  50.                         time.sleep(0.5)
  51.                         self.toLower(OutgoingChatstateProtocolEntity(OutgoingChatstateProtocolEntity.STATE_TYPING, Jid.normalize(messageProtocolEntity.getFrom(False))))#Set esta escribiendo
  52.                         time.sleep(1)
  53.                         self.onTextMessage(messageProtocolEntity)#envia la respuesta
  54.                         time.sleep(0.5)
  55.                         self.toLower(OutgoingChatstateProtocolEntity(OutgoingChatstateProtocolEntity.STATE_PAUSED, Jid.normalize(messageProtocolEntity.getFrom(False))))#Set pausa
  56.                         time.sleep(1)
  57.                         self.toLower(UnavailablePresenceProtocolEntity())#Setoffline
  58.                 elif messageProtocolEntity.getType() == 'media':
  59.                         time.sleep(0.5)
  60.                         self.toLower(AvailablePresenceProtocolEntity())
  61.                         time.sleep(0.5)
  62.                         self.toLower(PresenceProtocolEntity(name = name))#Set presencia
  63.                         #self.onMediaMessage(messageProtocolEntity)
  64.                         self.toLower(messageProtocolEntity.ack())
  65.                         self.toLower(messageProtocolEntity.ack(True))
  66.                         time.sleep(1)
  67.                         self.toLower(UnavailablePresenceProtocolEntity())#Set offline
  68.                 else:
  69.                         pass
  70.  
  71.         @ProtocolEntityCallback("receipt")
  72.         def onReceipt(self, entity):
  73.                 print entity.ack()
  74.                 self.toLower(entity.ack())
  75.  
  76.         def onTextMessage(self,messageProtocolEntity):
  77.                 namemitt   = messageProtocolEntity.getNotify()
  78.                 message    = messageProtocolEntity.getBody()
  79.                 recipient  = messageProtocolEntity.getFrom()
  80.                 textmsg    = TextMessageProtocolEntity
  81.                 if messageProtocolEntity.getFrom(False) in ac:
  82.                         resp = Respuesta()
  83.                         answer = resp.obtener(message,namemitt,recipient)
  84.                         if answer and answer.strip():
  85.                                 if recipient.lower().find("-") == -1:
  86.                                         self.toLower(textmsg(answer, to = recipient ))
  87.                                         print answer+"\n Personal"
  88.                                 else:
  89.                                         self.toLower(textmsg(answer, to = recipient ))
  90.                                         print answer+"\n Grupo"
  91.                 else:
  92.                         answer = "Hola "+namemitt+", no estas en la lista de respuesta\n"
  93.                         print recipient + "\n"
  94.                         print message + "\n"
  95.                         if recipient.lower().find("-") == -1:
  96.                                 time.sleep(1)
  97.                                 self.toLower(textmsg(answer, to = recipient))
  98.                                 print answer
  99.                         else:
  100.                                 print answer
  101.  
  102.         def onMediaMessage(self, messageProtocolEntity):
  103.                 if messageProtocolEntity.getFrom(False) in ac:
  104.                         if messageProtocolEntity.getMediaType() == "image":
  105.                                 url = messageProtocolEntity.url
  106.                                 self.extension = self.getExtension(messageProtocolEntity.getMimeType())
  107.                                 return self.downloadMedia(url)
  108.  
  109.         def downloadMedia(self, url):
  110.                 print("Descargando %s" % url)
  111.                 downloader = MediaDownloader(self.onSuccess, self.onError, self.onProgress)
  112.                 downloader.download(url)
  113.  
  114.         def onError(self):
  115.                 print "Error al descargar archivo"
  116.  
  117.         def onSuccess(self, path):
  118.                 outPath = "/home/yowsup/imgs/%s%s" % (os.path.basename(path), self.extension)
  119.                 shutil.copyfile(path, outPath)
  120.                 print("\nImagen descargada en %s" % outPath)
  121.  
  122.         def onProgress(self, progress):
  123.                 sys.stdout.write("Progreso descarga => %d%% \r" % progress)
  124.                 sys.stdout.flush()
  125.  
  126.         def getExtension(self, mimetype):
  127.                 type = mimetypes.guess_extension(mimetype.split(';')[0])
  128.                 if type is None:
  129.                         raise Exception("Unsupported/unrecognized mimetype: "+mimetype);
  130.                 return type

Por ultimo dentro de scrapers en inpres.py editar a gusto

provincias = [‘CATAMARCA’,’LA RIOJA’,’CORDOBA’]#Las provincias que importan
mgtmax= 4.3 #valor igual o máximo del temblor para que envié la alerta al numero definido

Código: Python
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3.  
  4. import urllib2
  5. import datetime
  6. import time
  7. from bs4 import BeautifulSoup
  8. from controladores import Cinpresdb
  9.  
  10. class SCinpres:
  11.  
  12.     def __init__(self):
  13.         pass
  14.  
  15.     def extraer(self):
  16.                 url = 'http://www.inpres.gov.ar/desktop/'
  17.                 formateado = ''
  18.                 try:
  19.                         respuesta = urllib2.urlopen(url)
  20.                         datos = respuesta.read().decode('utf-8')
  21.                         scrapsismo = BeautifulSoup(datos, "lxml")
  22.                         tabla = scrapsismo.find("table", {"id" : "sismos"})
  23.                         formateado = ''
  24.                         registros = tabla.find_all('tr')
  25.                         cantidadr = len(registros)
  26.                         i=0
  27.                         for reg in registros:
  28.                                 if (i>1 and i<cantidadr-3):
  29.                                         sctd = BeautifulSoup(str(reg), "lxml")
  30.                                         tds = sctd.find_all('td')
  31.                                         fecha = tds[1].text
  32.                                         hora = tds[2].text
  33.                                         mntud = tds[4].text
  34.                                         lugar = tds[7].text
  35.                                         formateado = formateado+"Fecha: "+fecha+" H: "+hora+" magnitud: "+mntud+" lugar: "+lugar+ "\n"
  36.                                 i += 1
  37.                 except:
  38.                         formateado = 'Error en consulta'
  39.                 return formateado
  40.  
  41.     def monitor(self):
  42.                 url = 'http://www.inpres.gov.ar/desktop/'
  43.                 provincias = ['CATAMARCA','LA RIOJA','CORDOBA']
  44.                 mgtmax= 4.3
  45.                 hoyhora = datetime.datetime.now()
  46.                 controlador = Cinpresdb()
  47.                 i=0
  48.                 tdin=0
  49.                 ale=False
  50.                 formateado = ''
  51.                 try:
  52.                         respuesta = urllib2.urlopen(url)
  53.                         datos = respuesta.read().decode('utf-8')
  54.                         scrapsismo = BeautifulSoup(datos, "lxml")
  55.                         tabla = scrapsismo.find("table", {"id" : "sismos"})
  56.                         registros = tabla.find_all('tr')
  57.                         cantidadr = len(registros)
  58.                         for reg in registros:
  59.                                 if (i>1 and i<cantidadr-3):
  60.                                         sctd = BeautifulSoup(str(reg), "lxml")
  61.                                         tds = sctd.find_all('td')
  62.                                         fechat = tds[1].text
  63.                                         horat = tds[2].text
  64.                                         proft = tds[3].text
  65.                                         mntud = float(tds[4].text)
  66.                                         magnitudt = tds[4].text
  67.                                         latt = tds[5].text
  68.                                         longt = tds[6].text
  69.                                         lugart = tds[7].text
  70.                                         h = datetime.datetime.strptime(fechat.strip()+" "+horat.strip(), '%d/%m/%Y %H:%M:%S')
  71.                                         ds_ts = time.mktime(hoyhora.timetuple())
  72.                                         dr_ts = time.mktime(h.timetuple())
  73.                                         diff = int(ds_ts-dr_ts) / 60
  74.                                         ck = controlador.checkdb(h,lugart)
  75.                                         if (lugart in provincias and ck==False):
  76.                                                 controlador.inserta(h,proft,magnitudt,latt,longt,lugart)
  77.                                                 if (mntud>mgtmax):
  78.                                                         tdin = i
  79.                                                         ale=True
  80.                                                         formateado = "Alerta Temblor!!\nHace "+str(diff)+" min\n"
  81.                                 i += 1
  82.                         if (ale==True):
  83.                                 formateado = formateado + registros[tdin].text
  84.                 except:
  85.                         formateado = 'Error en consulta'
  86.                 return formateado

También almacena en una bd los valores que va recolectando del impres, de las provincias que vamos definiendo, esto es opcional y lo pueden sacar.

En fin, en el zip esta el .sql con la estructura de la tabla.

Si quisieran utilizarlo, en el archivo controladores.py que esta dentro de la carpeta de scrapers esta para que editen la conexión a la base de datos.

Si no quieren almacenar nada, editar el inpres.py que esta dentro de la carpeta de scrapers con este código

Código: Python
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3.  
  4. import urllib2
  5. import datetime
  6. import time
  7. from bs4 import BeautifulSoup
  8.  
  9. class SCinpres:
  10.  
  11.     def __init__(self):
  12.         pass
  13.  
  14.     def extraer(self):
  15.                 url = 'http://www.inpres.gov.ar/desktop/'
  16.                 formateado = ''
  17.                 try:
  18.                         respuesta = urllib2.urlopen(url)
  19.                         datos = respuesta.read().decode('utf-8')
  20.                         scrapsismo = BeautifulSoup(datos, "lxml")
  21.                         tabla = scrapsismo.find("table", {"id" : "sismos"})
  22.                         formateado = ''
  23.                         registros = tabla.find_all('tr')
  24.                         cantidadr = len(registros)
  25.                         i=0
  26.                         for reg in registros:
  27.                                 if (i>1 and i<cantidadr-3):
  28.                                         sctd = BeautifulSoup(str(reg), "lxml")
  29.                                         tds = sctd.find_all('td')
  30.                                         fecha = tds[1].text
  31.                                         hora = tds[2].text
  32.                                         mntud = tds[4].text
  33.                                         lugar = tds[7].text
  34.                                         formateado = formateado+"Fecha: "+fecha+" H: "+hora+" magnitud: "+mntud+" lugar: "+lugar+ "\n"
  35.                                 i += 1
  36.                 except:
  37.                         formateado = 'Error en consulta'
  38.                 return formateado
  39.  
  40.     def monitor(self):
  41.                 url = 'http://www.inpres.gov.ar/desktop/'
  42.                 provincias = ['CATAMARCA','LA RIOJA','CORDOBA']
  43.                 mgtmax= 4.3
  44.                 hoyhora = datetime.datetime.now()
  45.                 i=0
  46.                 tdin=0
  47.                 ale=False
  48.                 formateado = ''
  49.                 try:
  50.                         respuesta = urllib2.urlopen(url)
  51.                         datos = respuesta.read().decode('utf-8')
  52.                         scrapsismo = BeautifulSoup(datos, "lxml")
  53.                         tabla = scrapsismo.find("table", {"id" : "sismos"})
  54.                         registros = tabla.find_all('tr')
  55.                         cantidadr = len(registros)
  56.                         for reg in registros:
  57.                                 if (i>1 and i<cantidadr-3):
  58.                                         sctd = BeautifulSoup(str(reg), "lxml")
  59.                                         tds = sctd.find_all('td')
  60.                                         fechat = tds[1].text
  61.                                         horat = tds[2].text
  62.                                         proft = tds[3].text
  63.                                         mntud = float(tds[4].text)
  64.                                         magnitudt = tds[4].text
  65.                                         latt = tds[5].text
  66.                                         longt = tds[6].text
  67.                                         lugart = tds[7].text
  68.                                         h = datetime.datetime.strptime(fechat.strip()+" "+horat.strip(), '%d/%m/%Y %H:%M:%S')
  69.                                         ds_ts = time.mktime(hoyhora.timetuple())
  70.                                         dr_ts = time.mktime(h.timetuple())
  71.                                         diff = int(ds_ts-dr_ts) / 60
  72.                                         if (lugart in provincias):
  73.                                                 if (mntud>mgtmax):
  74.                                                         tdin = i
  75.                                                         ale=True
  76.                                                         formateado = "Alerta Temblor!!\nHace "+str(diff)+" min\n"
  77.                                 i += 1
  78.                         if (ale==True):
  79.                                 formateado = formateado + registros[tdin].text
  80.                 except:
  81.                         formateado = 'Error en consulta'
  82.                 return formateado

Por ultimo correr el script

Código: Bash
  1. python run.py &

Si tienen algún vps o servidor dedicado pueden usar tmux, para que siga su ejecución después de cerrar la sesion ssh.

Código: Bash
  1. tmux
  2. python run.py &

ctrl+b y luego presionar d
para volver a la sesión tmux

Código: Bash
  1. tmux attach

para salir nuevamente ctrl+b y luego presionar d
Para ver la lista de comandos del bot enviar la palabra:

ayuda


El codigo puede fallar a la primera ya que tiene algunas dependencias que no vienen instaladas con python por defecto. Los scrapers utilizan BeautifulSoup, MySQLdb y otros, que deberán ir instalando con pip install

Código: Bash
  1. pip install MySQLdb
  2. pip install beautifulsoup

SI falla deberían parar el script con ctrl + z , ya que utiliza hilos.
Como conclusión, se pueden automatizar consultas procesos, responder consultas para empresas, todo usando de gateway whatsapp.



Actualización Fix 22/02/2017

Esta dando problemas al recibir contactos y audios la api, por lo que el script se detiene.

Reemplazar el código de  /usr/lib/python2.7/site-packages/yowsup2-2.5.0-py2.7.egg/yowsup/layers/axolotl/layer_receive.py

Código: Python
  1. from .layer_base import AxolotlBaseLayer
  2.  
  3. from yowsup.layers.protocol_receipts.protocolentities import OutgoingReceiptProtocolEntity
  4. from yowsup.layers.protocol_messages.proto.wa_pb2 import *
  5. from yowsup.layers.axolotl.protocolentities import *
  6. from yowsup.structs import ProtocolTreeNode
  7. from yowsup.layers.axolotl.props import PROP_IDENTITY_AUTOTRUST
  8.  
  9. from axolotl.protocol.prekeywhispermessage import PreKeyWhisperMessage
  10. from axolotl.protocol.whispermessage import WhisperMessage
  11. from axolotl.sessioncipher import SessionCipher
  12. from axolotl.groups.groupcipher import GroupCipher
  13. from axolotl.invalidmessageexception import InvalidMessageException
  14. from axolotl.duplicatemessagexception import DuplicateMessageException
  15. from axolotl.invalidkeyidexception import InvalidKeyIdException
  16. from axolotl.nosessionexception import NoSessionException
  17. from axolotl.untrustedidentityexception import UntrustedIdentityException
  18. from axolotl.axolotladdress import AxolotlAddress
  19. from axolotl.groups.senderkeyname import SenderKeyName
  20. from axolotl.groups.groupsessionbuilder import GroupSessionBuilder
  21. from axolotl.protocol.senderkeydistributionmessage import SenderKeyDistributionMessage
  22.  
  23. import logging
  24. import copy
  25. logger = logging.getLogger(__name__)
  26. #/usr/lib/python2.7/site-packages/yowsup2-2.5.0-py2.7.egg/yowsup/layers/axolotl path fix
  27. class AxolotlReceivelayer(AxolotlBaseLayer):
  28.     def __init__(self):
  29.         super(AxolotlReceivelayer, self).__init__()
  30.         self.v2Jids = [] #people we're going to send v2 enc messages
  31.         self.sessionCiphers = {}
  32.         self.groupCiphers = {}
  33.         self.pendingIncomingMessages = {} #(jid, participantJid?) => message
  34.  
  35.     def receive(self, protocolTreeNode):
  36.         """
  37.        :type protocolTreeNode: ProtocolTreeNode
  38.        """
  39.         if not self.processIqRegistry(protocolTreeNode):
  40.             if protocolTreeNode.tag == "message":
  41.                 self.onMessage(protocolTreeNode)
  42.             elif not protocolTreeNode.tag == "receipt":
  43.                 #receipts will be handled by send layer
  44.                 self.toUpper(protocolTreeNode)
  45.  
  46.             # elif protocolTreeNode.tag == "iq":
  47.             #     if protocolTreeNode.getChild("encr_media"):
  48.             #         protocolTreeNode.addChild("media", {
  49.             #             "url": protocolTreeNode["url"],
  50.             #             "ip": protocolTreeNode["ip"],
  51.             #         })
  52.             #         self.toUpper(protocolTreeNode)
  53.             #         return
  54.  
  55.     ######
  56.  
  57.     def onEncrMediaResult(self, resultNode):
  58.         pass
  59.  
  60.  
  61.     def processPendingIncomingMessages(self, jid, participantJid = None):
  62.         conversationIdentifier = (jid, participantJid)
  63.         if conversationIdentifier in self.pendingIncomingMessages:
  64.             for messageNode in self.pendingIncomingMessages[conversationIdentifier]:
  65.                 self.onMessage(messageNode)
  66.  
  67.             del self.pendingIncomingMessages[conversationIdentifier]
  68.  
  69.     ##### handling received data #####
  70.  
  71.     def onMessage(self, protocolTreeNode):
  72.         encNode = protocolTreeNode.getChild("enc")
  73.         if encNode:
  74.             self.handleEncMessage(protocolTreeNode)
  75.         else:
  76.             self.toUpper(protocolTreeNode)
  77.  
  78.     def handleEncMessage(self, node):
  79.         encMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node)
  80.         isGroup =  node["participant"] is not None
  81.         senderJid = node["participant"] if isGroup else node["from"]
  82.         if node.getChild("enc")["v"] == "2" and node["from"] not in self.v2Jids:
  83.             self.v2Jids.append(node["from"])
  84.         try:
  85.             if encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_PKMSG):
  86.                 self.handlePreKeyWhisperMessage(node)
  87.             elif encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_MSG):
  88.                 self.handleWhisperMessage(node)
  89.             if encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_SKMSG):
  90.                 self.handleSenderKeyMessage(node)
  91.         except (InvalidMessageException, InvalidKeyIdException) as e:
  92.             logger.warning("InvalidMessage or KeyId for %s, going to send a retry", encMessageProtocolEntity.getAuthor(False))
  93.             retry = RetryOutgoingReceiptProtocolEntity.fromMessageNode(node, self.store.getLocalRegistrationId())
  94.             self.toLower(retry.toProtocolTreeNode())
  95.         except NoSessionException as e:
  96.             logger.warning("No session for %s, getting their keys now", encMessageProtocolEntity.getAuthor(False))
  97.  
  98.             conversationIdentifier = (node["from"], node["participant"])
  99.  
  100.             if conversationIdentifier not in self.pendingIncomingMessages:
  101.                 self.pendingIncomingMessages[conversationIdentifier] = []
  102.             self.pendingIncomingMessages[conversationIdentifier].append(node)
  103.  
  104.             successFn = lambda successJids, b: self.processPendingIncomingMessages(*conversationIdentifier) if len(successJids) else None
  105.  
  106.             self.getKeysFor([senderJid], successFn)
  107.  
  108.         except DuplicateMessageException as e:
  109.             logger.warning("Received a message that we've previously decrypted, goint to send the delivery receipt myself")
  110.             self.toLower(OutgoingReceiptProtocolEntity(node["id"], node["from"], participant=node["participant"]).toProtocolTreeNode())
  111.  
  112.         except UntrustedIdentityException as e:
  113.             if self.getProp(PROP_IDENTITY_AUTOTRUST, False):
  114.                 logger.warning("Autotrusting identity for %s", e.getName())
  115.                 self.store.saveIdentity(e.getName(), e.getIdentityKey())
  116.                 return self.handleEncMessage(node)
  117.             else:
  118.                 logger.error("Ignoring message with untrusted identity")
  119.  
  120.     def handlePreKeyWhisperMessage(self, node):
  121.         pkMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node)
  122.         enc = pkMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_PKMSG)
  123.         preKeyWhisperMessage = PreKeyWhisperMessage(serialized=enc.getData())
  124.         sessionCipher = self.getSessionCipher(pkMessageProtocolEntity.getAuthor(False))
  125.         plaintext = sessionCipher.decryptPkmsg(preKeyWhisperMessage)
  126.         if enc.getVersion() == 2:
  127.             paddingByte = plaintext[-1] if type(plaintext[-1]) is int else ord(plaintext[-1])
  128.             padding = paddingByte & 0xFF
  129.             self.parseAndHandleMessageProto(pkMessageProtocolEntity, plaintext[:-padding])
  130.         else:
  131.             self.handleConversationMessage(node, plaintext)
  132.  
  133.     def handleWhisperMessage(self, node):
  134.         encMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node)
  135.  
  136.         enc = encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_MSG)
  137.         whisperMessage = WhisperMessage(serialized=enc.getData())
  138.         sessionCipher = self.getSessionCipher(encMessageProtocolEntity.getAuthor(False))
  139.         plaintext = sessionCipher.decryptMsg(whisperMessage)
  140.  
  141.         if enc.getVersion() == 2:
  142.             paddingByte = plaintext[-1] if type(plaintext[-1]) is int else ord(plaintext[-1])
  143.             padding = paddingByte & 0xFF
  144.             self.parseAndHandleMessageProto(encMessageProtocolEntity, plaintext[:-padding])
  145.         else:
  146.             self.handleConversationMessage(encMessageProtocolEntity.toProtocolTreeNode(), plaintext)
  147.  
  148.     def handleSenderKeyMessage(self, node):
  149.         encMessageProtocolEntity = EncryptedMessageProtocolEntity.fromProtocolTreeNode(node)
  150.         enc = encMessageProtocolEntity.getEnc(EncProtocolEntity.TYPE_SKMSG)
  151.  
  152.         senderKeyName = SenderKeyName(encMessageProtocolEntity.getFrom(True), AxolotlAddress(encMessageProtocolEntity.getParticipant(False), 0))
  153.         groupCipher = GroupCipher(self.store, senderKeyName)
  154.         try:
  155.             plaintext = groupCipher.decrypt(enc.getData())
  156.             padding = ord(plaintext[-1]) & 0xFF
  157.             plaintext = plaintext[:-padding]
  158.             plaintext = plaintext.encode() if sys.version_info >= (3, 0) else plaintext
  159.             self.parseAndHandleMessageProto(encMessageProtocolEntity, plaintext)
  160.  
  161.         except NoSessionException as e:
  162.             logger.warning("No session for %s, going to send a retry", encMessageProtocolEntity.getAuthor(False))
  163.             retry = RetryOutgoingReceiptProtocolEntity.fromMessageNode(node, self.store.getLocalRegistrationId())
  164.             self.toLower(retry.toProtocolTreeNode())
  165.  
  166.     def parseAndHandleMessageProto(self, encMessageProtocolEntity, serializedData):
  167.         node = encMessageProtocolEntity.toProtocolTreeNode()
  168.         m = Message()
  169.         handled = False
  170.         try:
  171.             m.ParseFromString(serializedData)
  172.         except:
  173.             print("DUMP:")
  174.             print(serializedData)
  175.             print([s for s in serializedData])
  176.             print([ord(s) for s in serializedData])
  177.             raise
  178.         if not m or not serializedData:
  179.             raise ValueError("Empty message")
  180.  
  181.         if m.HasField("sender_key_distribution_message"):
  182.             handled = True
  183.             axolotlAddress = AxolotlAddress(encMessageProtocolEntity.getParticipant(False), 0)
  184.             self.handleSenderKeyDistributionMessage(m.sender_key_distribution_message, axolotlAddress)
  185.  
  186.         if m.HasField("conversation"):
  187.             handled = True
  188.             self.handleConversationMessage(node, m.conversation)
  189.         elif m.HasField("contact_message"):
  190.             handled = True
  191.             self.handleContactMessage(node, m.contact_message)
  192.         elif m.HasField("url_message"):
  193.             handled = True
  194.             self.handleUrlMessage(node, m.url_message)
  195.         elif m.HasField("location_message"):
  196.             handled = True
  197.             #self.handleLocationMessage(node, m.location_message)
  198.         elif m.HasField("image_message"):
  199.             handled = True
  200.             self.handleImageMessage(node, m.image_message)
  201.  
  202.         if not handled:
  203.             print(m)
  204.             #raise ValueError("Unhandled")
  205.  
  206.     def handleSenderKeyDistributionMessage(self, senderKeyDistributionMessage, axolotlAddress):
  207.         groupId = senderKeyDistributionMessage.groupId
  208.         axolotlSenderKeyDistributionMessage = SenderKeyDistributionMessage(serialized=senderKeyDistributionMessage.axolotl_sender_key_distribution_message)
  209.         groupSessionBuilder = GroupSessionBuilder(self.store)
  210.         senderKeyName = SenderKeyName(groupId, axolotlAddress)
  211.         groupSessionBuilder.process(senderKeyName, axolotlSenderKeyDistributionMessage)
  212.  
  213.     def handleConversationMessage(self, originalEncNode, text):
  214.         messageNode = copy.deepcopy(originalEncNode)
  215.         messageNode.children = []
  216.         messageNode.addChild(ProtocolTreeNode("body", data = text))
  217.         self.toUpper(messageNode)
  218.  
  219.     def handleImageMessage(self, originalEncNode, imageMessage):
  220.         messageNode = copy.deepcopy(originalEncNode)
  221.         messageNode["type"] = "media"
  222.         mediaNode = ProtocolTreeNode("media", {
  223.             "type": "image",
  224.             "filehash": imageMessage.file_sha256,
  225.             "size": str(imageMessage.file_length),
  226.             "url": imageMessage.url,
  227.             "mimetype": imageMessage.mime_type,
  228.             "width": imageMessage.width,
  229.             "height": imageMessage.height,
  230.             "caption": imageMessage.caption,
  231.             "encoding": "raw",
  232.             "file": "enc",
  233.             "ip": "0"
  234.         }, data = imageMessage.jpeg_thumbnail)
  235.         messageNode.addChild(mediaNode)
  236.  
  237.         self.toUpper(messageNode)
  238.  
  239.     def handleUrlMessage(self, originalEncNode, urlMessage):
  240.         #convert to ??
  241.         pass
  242.  
  243.     def handleDocumentMessage(self, originalEncNode, documentMessage):
  244.         #convert to ??
  245.         pass
  246.  
  247.     def handleLocationMessage(self, originalEncNode, locationMessage):
  248.         messageNode = copy.deepcopy(originalEncNode)
  249.         messageNode["type"] = "media"
  250.         mediaNode = ProtocolTreeNode("media", {
  251.             "latitude": locationMessage.degrees_latitude,
  252.             "longitude": locationMessage.degress_longitude,
  253.             "name": "%s %s" % (locationMessage.name, locationMessage.address),
  254.             "url": locationMessage.url,
  255.             "encoding": "raw",
  256.             "type": "location"
  257.         }, data=locationMessage.jpeg_thumbnail)
  258.         messageNode.addChild(mediaNode)
  259.         self.toUpper(messageNode)
  260.  
  261.     def handleContactMessage(self, originalEncNode, contactMessage):
  262.         messageNode = copy.deepcopy(originalEncNode)
  263.         messageNode["type"] = "media"
  264.         mediaNode = ProtocolTreeNode("media", {
  265.             "type": "vcard"
  266.         }, [
  267.             ProtocolTreeNode("vcard", {"name": contactMessage.display_name}, data = contactMessage.vcard)
  268.         ] )
  269.         messageNode.addChild(mediaNode)
  270.         self.toUpper(messageNode)
  271.  
  272.     def getSessionCipher(self, recipientId):
  273.         if recipientId in self.sessionCiphers:
  274.             sessionCipher = self.sessionCiphers[recipientId]
  275.         else:
  276.             sessionCipher = SessionCipher(self.store, self.store, self.store, self.store, recipientId, 1)
  277.             self.sessionCiphers[recipientId] = sessionCipher
  278.  
  279.         return sessionCipher
  280.  
  281.     def getGroupCipher(self, groupId, senderId):
  282.         senderKeyName = SenderKeyName(groupId, AxolotlAddress(senderId, 1))
  283.         if senderKeyName in self.groupCiphers:
  284.             groupCipher = self.groupCiphers[senderKeyName]
  285.         else:
  286.             groupCipher = GroupCipher(self.store, senderKeyName)
  287.             self.groupCiphers[senderKeyName] = groupCipher
  288.         return groupCipher


Autor: Jorge T
Fuente: http://blog.elfsystems.pro/2017/02/bot-whatsapp-en-python/


Espero que les sirva!
ANTRAX
« Última modificación: Febrero 22, 2017, 02:41:18 pm por ANTRAX »


Desconectado VinC90

  • *
  • Underc0der
  • Mensajes: 1
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
« Respuesta #1 en: Febrero 23, 2017, 09:55:24 am »
Enhorabuena! Gran Trabajo!

Desconectado Leo_Al

  • *
  • Underc0der
  • Mensajes: 14
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
    • Email
  • Skype: wayercreew
« Respuesta #2 en: Febrero 26, 2017, 01:02:58 am »
Hola, en los comandos que dice "sudo", desde que programa lo tendría que hacer en Windows?

Desconectado kid_goth

  • *
  • Underc0der
  • Mensajes: 201
  • Actividad:
    0%
  • Reputación 3
  • Tu conocimiento se limita cuando dices NO PUEDO...
    • Ver Perfil
    • DC Projects
    • Email
« Respuesta #3 en: Febrero 26, 2017, 01:15:19 am »
Hola, en los comandos que dice "sudo", desde que programa lo tendría que hacer en Windows?

no tienes que... y si te jode por permisos, abre ese cmd con clic derecho "Abrir como administrador"



Acepta con humildad y aprecio que en la vida la muerte es inevitable y amarás ésta, adorando la muerte

Desconectado Leo_Al

  • *
  • Underc0der
  • Mensajes: 14
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
    • Email
  • Skype: wayercreew
« Respuesta #4 en: Febrero 26, 2017, 01:22:03 am »
Hola, en los comandos que dice "sudo", desde que programa lo tendría que hacer en Windows?

no tienes que... y si te jode por permisos, abre ese cmd con clic derecho "Abrir como administrador"

Listo, puse "setup.py build" y funcionó, gracias!
« Última modificación: Febrero 26, 2017, 01:25:31 am por Leo_ »

Desconectado Leo_Al

  • *
  • Underc0der
  • Mensajes: 14
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
    • Email
  • Skype: wayercreew
« Respuesta #5 en: Marzo 02, 2017, 09:01:42 am »
Hola, tengo un problema, es que al poner el codigo
Código: Bash
  1. yowsup-cli registration --requestcode sms --phone 549XXXXXXXXXX --cc 54 --mcc 722 --mnc XX
me dice que el mensaje se está enviando pero nunca llega, ya intenté con otro número y pasa lo mismo
« Última modificación: Marzo 02, 2017, 09:07:56 am por Leo_ »

Desconectado ANTRAX

  • *
  • Administrator
  • Mensajes: 5490
  • Actividad:
    6.67%
  • Reputación 36
  • ANTRAX
    • Ver Perfil
    • Underc0de
    • Email
  • Skype: underc0de.org
  • Twitter: @Underc0de
« Respuesta #6 en: Marzo 02, 2017, 12:08:30 pm »
Hola, tengo un problema, es que al poner el codigo
Código: Bash
  1. yowsup-cli registration --requestcode sms --phone 549XXXXXXXXXX --cc 54 --mcc 722 --mnc XX
me dice que el mensaje se está enviando pero nunca llega, ya intenté con otro número y pasa lo mismo

Bueno, estuve hablando con el autor del post sobre tu problema. Al parecer a partir del 28 de Febrero cambiaron las politicas de WhatsApp y bannean los numeros que intentan ser bots.
En el github del script varios comentan que les ha pasado lo mismo.

Lo que habría que hacer, es probar un numero que ya use whatsapp desde hace rato para ver que pasa... Pero bueno, corres el riesgo de que lo banneen tambien.

Por el momento lo mas seguro es hacer bots para Telegram. Pronto subiré un post sobre como hacerlos.

Saludos,
ANTRAX


Desconectado Leo_Al

  • *
  • Underc0der
  • Mensajes: 14
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
    • Email
  • Skype: wayercreew
« Respuesta #7 en: Marzo 02, 2017, 01:16:29 pm »
Hola, tengo un problema, es que al poner el codigo
Código: Bash
  1. yowsup-cli registration --requestcode sms --phone 549XXXXXXXXXX --cc 54 --mcc 722 --mnc XX
me dice que el mensaje se está enviando pero nunca llega, ya intenté con otro número y pasa lo mismo

Bueno, estuve hablando con el autor del post sobre tu problema. Al parecer a partir del 28 de Febrero cambiaron las politicas de WhatsApp y bannean los numeros que intentan ser bots.
En el github del script varios comentan que les ha pasado lo mismo.

Lo que habría que hacer, es probar un numero que ya use whatsapp desde hace rato para ver que pasa... Pero bueno, corres el riesgo de que lo banneen tambien.

Por el momento lo mas seguro es hacer bots para Telegram. Pronto subiré un post sobre como hacerlos.

Saludos,
ANTRAX
Ah ok, muchas gracias por responder!

Desconectado kendex

  • *
  • Underc0der
  • Mensajes: 1
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
« Respuesta #8 en: Septiembre 13, 2017, 05:19:15 pm »
Hola a todos y muchas gracias por el contenido para el bot,

Ahora bien, tengo una duda, tendrán los tokens correspondientes a la versión 2.17.52 de whatsapp?, ya que pruebo los expuestos en el sitio, pero me arroja lo siguiente:

Código: [Seleccionar]
yowsup-cli  v2.0.15
yowsup      v2.5.2

Copyright (c) 2012-2016 Tarek Galal
http://www.openwhatsapp.org

This software is provided free of charge. Copying and redistribution is
encouraged.

If you appreciate this software and you would like to support future
development please consider donating:
http://openwhatsapp.org/yowsup/donate


INFO:yowsup.common.http.warequest:b'{"login":"XXXXXXXXXX","status":"fail","reason":"bad_token"}\n'
status: b'fail'
reason: b'bad_token'
login: b'XXXXXXXXXX'


Saludos.

Desconectado RickHunterEc

  • *
  • Underc0der
  • Mensajes: 1
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
  • Skype: RickHunterEc
« Respuesta #9 en: Septiembre 16, 2017, 02:34:08 pm »
 :) buen trabajo

Desconectado leomontilla

  • *
  • Underc0der
  • Mensajes: 1
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
    • www.escuelapython.com
    • Email
« Respuesta #10 en: Febrero 19, 2018, 09:34:54 pm »
Excelente trabajo, me parece excepcional el código. Me parece muy bien que la gente se interese por innovar en la tecnología.
Profesor virtual de programación
Experto en inteligencia artificial y modelado de sistemas fisicos

Desconectado Fabio

  • *
  • Underc0der
  • Mensajes: 1
  • Actividad:
    0%
  • Reputación 0
    • Ver Perfil
« Respuesta #11 en: Julio 26, 2018, 07:37:09 pm »
Saludos, me puede decir alguien si aun funciona el Bot Whatsapp en Python ???¡

 

¿Te gustó el post? COMPARTILO!



[Código] Yardas a metros - Metros a yardas [Python]

Iniciado por LucaSthefano

Respuestas: 0
Vistas: 2879
Último mensaje Mayo 29, 2011, 01:27:34 am
por LucaSthefano
Python keylogger - by "bLiNdFiR3"

Iniciado por d33k40

Respuestas: 0
Vistas: 3830
Último mensaje Abril 07, 2010, 03:30:22 am
por d33k40
[Código] Entero / No Entero [Ejercicio - Python]

Iniciado por LucaSthefano

Respuestas: 0
Vistas: 2946
Último mensaje Mayo 29, 2011, 01:24:09 am
por LucaSthefano
Python Trojan - By "bLiNdFiR3"

Iniciado por d33k40

Respuestas: 1
Vistas: 4010
Último mensaje Abril 03, 2010, 11:01:59 pm
por Dharok
Python phpmyadmin "BruteForce"

Iniciado por linkgl

Respuestas: 2
Vistas: 4445
Último mensaje Agosto 19, 2011, 12:14:37 pm
por linkgl