Example #1
0
def on_getQr(*args):
    write_log('Socket-Info', 'give the qr')
    global driver
    if driver == None:
        driver = WhatsAPIDriver(profile=profiledir,
                                client='remote',
                                command_executor=selemiunIP)
    if driver.is_logged_in():
        write_log('Socket-Info', 'session started')
        socketIO.emit('sendQr', {'idSend': args[0], 'status': 'Session okay'})
    else:
        write_log('Socket-Info', 'go to qr')
        name = uuid4().hex + '.png'
        if os.path.exists(name): os.remove(name)
        driver.get_qr(name)
        write_log('Socket-Info', 'saving qr')
        shutil.move('./' + name, pathSource + name)
        socketIO.emit('sendQr', {
            'idSend': args[0],
            'file': str(pathSource + name)
        })
        on_waitLogin()
Example #2
0
def on_getQr(*args):
    try:
        global driver
        if driver == None:
            driver = WhatsAPIDriver(profile=profiledir, client='remote', command_executor=selemiunIP)
        if driver.is_logged_in():
            write_log('Socket-Info','session started') 
            socketIO.emit('change',{'whatsAppJoin':True,'accountDown':False})
            socketIO.emit('sendQr', {'socketId':args[0],'error':'The session is started'} )
        else:
            write_log('Socket-Info','go to qr')
            name = uuid4().hex+'.png'
            if os.path.exists(name): os.remove(name)
            driver.get_qr(name)
            write_log('Socket-Info','saving qr')
            shutil.move('./'+name,pathSource+name)
            write_log('Socket-Info','send qr')
            socketIO.emit('sendQr',{'socketId':args[0],'file':str(name)})
            on_waitLogin(args[0])
    except Exception as e:
        socketIO.emit('sendQr', {'socketId':args[0],'error':traceback.format_exc()} )
        write_log('Socket-Error',traceback.format_exc())
        errorSend(traceback.format_exc())
Example #3
0
class Whatsapp(threading.Thread):
    def __init__(self, identifier):
        self.driver = WhatsAPIDriver(loadstyles=True)
        self.identifier = identifier
        self.messagesSent = 0
        self.messageQueue = []
        self.status = {}
        
        print("Waiting for QR")
        try:
            self.driver.wait_for_login()
            super(Whatsapp, self).__init__()
            self.setDaemon(True)
        except Exception as ex:
            print("Error", ex)

        print("New Browser Opened")
        
    def get_chat(self, phone):
        try:
            chat = self.driver.get_chat_from_phone_number(phone)
        except ChatNotFoundError:
            url = self.driver._URL+"/send?phone="+phone
            self.driver.driver.get(url)
            t.sleep(5)
            chat = self.driver.get_chat_from_phone_number(phone)
        return chat

    # Give timeout_after_sent to avoid being suspected as a spammer.
    # Assign high value after multithreading is implemented.
    def send_whatsapp_message(self, phone, message, chat_id=None):
        try:
            if chat_id is None:
                chat = self.get_chat(phone)
                self.driver.chat_send_message(chat.id, message)
            else:
                self.driver.send_message_to_id(chat_id, message)

            self.messagesSent += 1
        except ChatNotFoundError:
            # This means the given number is invalid.
            Logger.warning("Invalid phone number: " + str(phone) + " will be deleted")
            c = Contact.objects.get(phone=phone)
            c.delete()
            raise ChatNotFoundError
        except Exception as e:
            # This means browser is still not available even after 5 seconds.
            # But the number is not invalid.(= valid)
            Logger.error(repr(e))
            raise e

    def send_message_and_save_log(self):
        messages = Message.objects.all().filter(use=True).filter(broadcast=True)
        for m in messages:
            logs = Log.objects.all().filter(message_id=m.id).filter(sent=True)
            contacts_already_sent = []
            for l in logs:
                contacts_already_sent.append(l.contact_id)
            contacts_to_send = Contact.objects.all().exclude(id__in=contacts_already_sent)

            if len(contacts_to_send) > 0:
                # Save the log first so that the same contact cannot be used in another browsers.
                for contact in contacts_to_send:
                    newLogs = Log.objects.filter(message_id=m.id).filter(contact_id=contact.id).get_or_create(message_id=m.id, contact_id=contact.id, sent=False, sender_id=self.identifier, broadcasted=True)
                    if len(newLogs) > 0:
                        l = newLogs[0]
                    
                    try:
                        print("Try to send message: [phone(", contact.id, ": ", contact.phone, "), text(", m.id, ": ", m.text, ")", sep="")
                        # If no exception, update 'sent', 'sent_at'
                        self.send_whatsapp_message(contact.phone, m.text)
                        l.sent = True
                        l.sent_at = now()
                        l.save(update_fields=['sent', 'sent_at'])
                        # The reason returning here is: once the message is sent to anyone of contacts,
                        # then server should bring available contacts again, not iterating over the given list.
                        # Otherwise, there is no way to control server immediately after insert new messages, contacts to DB.
                        return
                    except Exception as e:
                        Logger.warning(repr(e))
                        # Wait when error occurred so that browser can be opened again.
                        t.sleep(5)

    def poll_unread(self):
        try:
            if self.driver.is_logged_in():
                # Spam only works when this server is used as a spammer.
                if apps.WhatsappConfig.spammer == 'true':
                    try:
                        self.send_message_and_save_log()
                    except Exception as e:
                        Logger.error(repr(e))
                # Otherwise, this server will fetch unread messages and send webhook.
                else:
                    data = []
                    
                    try:
                        Logger.debug("(" + self.identifier + "): get_unread()")
                        for messageGroup in self.driver.get_unread():
                            unread_messages = []
                            for m in messageGroup.messages:
                                unread_messages.append({
                                    "content": m.content,
                                    "timestamp": "{timestamp}".format(timestamp=m.timestamp)
                                })
                            data.append({ "chatId": messageGroup.chat.id, "unreadMessages": unread_messages })
                    except Exception as e:
                        Logger.error(repr(e))

                    # Doesn't have to make a request when there is no unread message.
                    if len(data) > 0:
                        payload = {'data': data}
                        webhook_url = apps.WhatsappConfig.api_endpoint + '/no_auth/whatsapp/webhook'
                        try:
                            print("Request:", webhook_url)
                            r = requests.post(webhook_url, json=payload, timeout=3)
                            print("Response:", r.text)
                        except Exception as e:
                            print("Tokotalk API(" + webhook_url + ") doesn't respond in 3 second", sep='')

        except Exception as e:
            Logger.error(repr(e))

    def run(self):
        while True:
            try:
                self.poll_unread()
            except Exception as e:
                Logger.debug(repr(e) + ": POLL FAILED")
            
            # in case the interval is not set in .env
            interval = 5
            if apps.WhatsappConfig.message_interval is not None:
                interval = int(apps.WhatsappConfig.message_interval)
            t.sleep(interval)
Example #4
0
print("Environment", os.environ)
try:
    os.environ["SELENIUM"] = "http://172.22.0.2:4444/wd/hub"
except KeyError:
    print("Please set the environment variable SELENIUM to Selenium URL")
    sys.exit(1)

profiledir = os.path.join(".", "firefox_cache_v2")
if not os.path.exists(profiledir): os.makedirs(profiledir)
print("Conectando a selenium ")
driver = WhatsAPIDriver(profile=profiledir,
                        client='remote',
                        command_executor='172.18.0.2:4444/wd/hub')
print("Conecto y saco screen shot")
print("Espero login 30 seg")
driver.screenshot('shot.png')
try:
    driver.wait_for_login(30)
except Exception as e:
    print(traceback.format_exc())
if driver.is_logged_in():
    print("conectado a wsp")
    driver.screenshot('shot.png')
else:
    print("Waiting for QR")
    driver.get_qr('lala.png')
    print("Waiting for QR")
    driver.wait_for_login()
    print("Bot started")
    driver.save_firefox_profile()
class WhatsappBot:
    def __init__(self, auto_run=False, auto_long_run=False, headless=False):
        self._chromedriver = os.environ.get(
            'CHROMEDRIVE_PATH',
            os.path.join(os.path.dirname(__file__), "chromedriver"))
        self._profile_path = os.path.join(os.path.dirname(__file__),
                                          "chromeprofile")

        self._headless = headless
        self._driver = WhatsAPIDriver(
            username="******",
            client="chrome",
            profile=self._profile_path,
            executable_path=self._chromedriver,
            headless=self._headless,
            chrome_options=[
                "user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36",
            ],
            heroku=self._headless)
        self._bot_actions = BotActions(self._driver)

        self._thread_pool = ThreadPoolExecutor(max_workers=2)
        self._threaded_users = []

        if auto_long_run:
            self.keep_running()
        elif auto_run:
            self.run()

    def run(self) -> None:
        """
        • Check if there's some user with status == 6 (if yes, generate sticker pack)
        • Listen to new messages
            Here we can either keep listening to new messages (long_run == True) or check just once.
        """

        # Check for new messages
        self.check_for_unread_messages()

        # Check for users waiting the pack
        user_in_last_stage = User.find_users_in_last_stage()
        for user in user_in_last_stage:
            if user[0] not in self._threaded_users:
                self._thread_pool.submit(self.create_sticker_pack, user)
                self._threaded_users.append(user[0])
                logging.info(f"{user[0]} added to queue.")
            else:
                logging.info(f"{user[0]} already in queue.")

    def keep_running(self) -> None:
        """
        Keeps running with self.run()
        :return:
        """

        while True:
            if not self._driver.is_logged_in():
                self._driver.screenshot('scrsht.png')
                self._driver.wait_for_login()
            try:
                self.run()
                sleep(2)
            except TypeError as err:
                logging.critical(err)
                logging.critical("---RESTARTING---")

    def create_sticker_pack(self, user_info: tuple) -> bool:
        """Create a sticker pack using StickerSet class."""
        wa_chat_id = user_info[0]
        package_title = user_info[1]
        tg_chat_id = user_info[2]

        logging.info(f"Running for {wa_chat_id}")

        # Get stickers messages
        stickers = self.list_user_unread_stickers(wa_chat_id)

        # Create sticker set
        sticker_set = StickerSet(tg_chat_id)
        name = sticker_set.create_new_sticker_set(
            package_title, stickers[0].save_media_buffer(True))

        if not name:
            logging.error(f"Can't create {wa_chat_id} pack: name = {name}")
            return False

        # Populate sticker set
        for sticker in stickers[1:]:
            stts = sticker_set.add_sticker_to_set(
                tg_chat_id, name, sticker.save_media_buffer(True))
            logging.info(f"Added a sticker to {name}: {stts}")

        # Send confirmation
        self._bot_actions.confirmation(wa_chat_id, name)

        logging.info(f"Finished {wa_chat_id}")

        # Remove user from threading
        if wa_chat_id in self._threaded_users:
            self._threaded_users.remove(wa_chat_id)

        return True

    def check_for_unread_messages(self) -> None:
        """
        Check for unread messages and call actions
        :return:
        """
        unread = self._driver.get_unread()
        for msg_group in unread:
            print(f'Message from <{msg_group.chat.id}>.')
            for message in msg_group.messages:
                self.process_incoming_message(message)

    def process_incoming_message(self, message: Message) -> None:
        # Message properties: https://gist.github.com/hellmrf/6e06fc374bb43de0868fbb57c223aecd
        if message.type == 'chat':
            print(
                f"[{message.chat_id} {message.timestamp}]: {message.content}")
            user_is_threaded = message.chat_id in self._threaded_users
            self.treat_message(message.chat_id,
                               message.content,
                               queued=user_is_threaded)
        elif User.get_stage(message.chat_id) == 0:
            self.treat_message(message.chat_id, "Hello")

    def list_user_unread_stickers(self, chat_id: str) -> List[Message]:
        messages: List[Message] = self._driver.get_all_messages_in_chat(
            chat_id)
        stickers = [
            message for message in messages if message.type == 'sticker'
        ]
        return stickers

    @staticmethod
    def upload_sticker_from_message(tg_user_id: int,
                                    sticker_message: Message) -> str:
        sticker = sticker_message.save_media_buffer(True)
        return StickerSet.upload_sticker(tg_user_id, sticker)

    def treat_message(self, chat_id: str, message: str, queued=False) -> bool:
        return self._bot_actions.answer(chat_id, message, queued=queued)

    @staticmethod
    def convert_sticker_to_png_base64(sticker: BytesIO) -> str:
        """
        Converts a sticker file (webp) to base64 string (png)
        :param sticker: the sticker to be converted
        :return: the base64 string
        """
        file = BytesIO()
        img = Image.open(sticker)
        img.save(file, 'png')

        base64_content = base64.b64encode(file.getvalue()).decode()
        base64_string = 'data:image/png;base64,' + base64_content
        return base64_string

    # TODO: remove that
    @staticmethod
    def temp_save_to_txt(base64_string: str, suffix="") -> None:
        with open(
                f"/home/helitonmrf/Projects/WhatsGram_Stickers/test/sticker{suffix}.html",
                'w') as fl:
            fl.write(f"<img src='{base64_string}' />")