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()
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())
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)
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}' />")