Skip to content

Commit

Permalink
improve close
Browse files Browse the repository at this point in the history
  • Loading branch information
3mora2 committed Jul 17, 2024
1 parent 199cc80 commit b9f5c81
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 47 deletions.
76 changes: 63 additions & 13 deletions WPP_Whatsapp/api/layers/HostLayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pathlib import Path
from typing import Callable

from playwright._impl._errors import TargetClosedError
from playwright.async_api import Page
from WPP_Whatsapp.api.const import whatsappUrl, Logger
from WPP_Whatsapp.api.helpers.function import asciiQr
Expand All @@ -27,6 +28,7 @@ class HostLayer:
isInitialized: bool
isInjected: bool
isStarted: bool
isClosed: bool
isLogged: bool
isInChat: bool
urlCode: str
Expand All @@ -47,6 +49,7 @@ class HostLayer:
def __init__(self):
self.isInChat = False
self.isLogged = False
self.isClosed = False
self.__initialize()

def catchQR(self, **kwargs):
Expand All @@ -65,12 +68,21 @@ def __initialize(self):
self.isInitialized = True

async def on_close(self, _):
self.isClosed = True
self.logger.info(f'{self.session}: Page Closed')
self.cancelAutoClose()

async def on_load(self, _):
self.logger.info(f'{self.session}: Page loaded')
await self._afterPageLoad()
if self.isClosed:
return
try:
self.logger.info(f'{self.session}: Page loaded')
await self._afterPageLoad()
except (RuntimeError, TargetClosedError):
# mean stop app
self.logger.info(f'{self.session}: Stop App, Auto Close')
self.isClosed = True
await self.tryAutoClose()

async def _afterPageLoad(self):
self.logger.info(f'{self.session}: Injecting wapi.js')
Expand Down Expand Up @@ -197,6 +209,9 @@ async def __handel_request(self, ):

#################################################################################################
async def __checkStart(self):
if self.isClosed and hasattr(self, "checkStartInterval"):
self.clearInterval(self.checkStartInterval)
return
await self.__needsToScan()

async def __checkQrCode(self):
Expand Down Expand Up @@ -257,27 +272,50 @@ async def __checkInChat(self):
self.statusFind('inChat', self.session)

async def tryAutoClose(self):
if self.isClosed:
self.logger.info(f'{self.session}: Closing the page')
self.statusFind('autocloseCalled', self.session)
if not self.page.is_closed():
await self.ThreadsafeBrowser.close()

if not hasattr(self, "autoCloseInterval"):
return

if self.autoCloseInterval:
self.cancelAutoClose()

if (self.autoClose > 0 or self.options.get(
"deviceSyncTimeout") > 0) and (
not self.autoCloseInterval or self.autoCloseInterval.is_set()) and not self.page.is_closed():
not self.autoCloseInterval or self.autoCloseInterval.is_set()):

self.logger.info(f'{self.session}: Closing the page')
self.autoCloseCalled = True

self.isClosed = True
self.statusFind('autocloseCalled', self.session)
if not self.page.is_closed():
await self.ThreadsafeBrowser.close()

def sync_tryAutoClose(self):
if self.isClosed:
self.logger.info(f'{self.session}: Closing the page')
self.statusFind('autocloseCalled', self.session)
if not self.page.is_closed():
self.ThreadsafeBrowser.sync_close()

if not hasattr(self, "autoCloseInterval"):
return

if self.autoCloseInterval:
self.cancelAutoClose()

if (self.autoClose > 0 or self.options.get(
"deviceSyncTimeout") > 0) and (
not self.autoCloseInterval or self.autoCloseInterval.is_set()) and not self.page.is_closed():
not self.autoCloseInterval or self.autoCloseInterval.is_set()):
self.logger.info(f'{self.session}: Closing the page')
self.autoCloseCalled = True

self.isClosed = True
self.statusFind('autocloseCalled', self.session)
if not self.page.is_closed():
self.ThreadsafeBrowser.sync_close()
Expand Down Expand Up @@ -317,6 +355,10 @@ async def autoCloseIntervalHandel(self):
self.cancelAutoClose()
return

if not self.isStarted or self.isClosed:
self.cancelAutoClose()
return

self.remain -= 1
if self.remain % 10 == 0 or self.remain <= 5:
self.logger.info(f'{self.session}: http => Auto close remain: {self.remain}s')
Expand All @@ -341,7 +383,7 @@ async def __getQrCode(self):
def waitForQrCodeScan(self):
if not self.isStarted:
raise Exception('waitForQrCodeScan error: Session not started')
while not self.page.is_closed() and not self.isLogged:
while not self.page.is_closed() and not self.isLogged and not self.isClosed:
# sleep(200 / 1000)
self.ThreadsafeBrowser.sleep(0.2)
needScan = self.__sync_needsToScan()
Expand All @@ -350,7 +392,7 @@ def waitForQrCodeScan(self):
async def waitForQrCodeScan_(self):
if not self.isStarted:
raise Exception('waitForQrCodeScan error: Session not started')
while not self.page.is_closed() and not self.isLogged:
while not self.page.is_closed() and not self.isLogged and not self.isClosed:
# sleep(200 / 1000)
await asyncio.sleep(200 / 1000)
needScan = await self.__needsToScan()
Expand All @@ -365,7 +407,7 @@ def waitForInChat(self):
return False

start = datetime.now()
while not self.page.is_closed() and self.isLogged and not self.isInChat:
while not self.page.is_closed() and self.isLogged and not self.isInChat and not self.isClosed:
if 0 < self.options.get("deviceSyncTimeout") <= (datetime.now() - start).seconds:
Logger.info(f"deviceSyncTimeout:{self.options.get('deviceSyncTimeout')} timeout")
return False
Expand All @@ -387,7 +429,7 @@ async def waitForInChat_(self):
return False

start = datetime.now()
while not self.page.is_closed() and self.isLogged and not self.isInChat:
while not self.page.is_closed() and self.isLogged and not self.isInChat and not self.isClosed:
if 0 < self.options.get("deviceSyncTimeout") <= (datetime.now() - start).seconds:
Logger.info(f"deviceSyncTimeout:{self.options.get('deviceSyncTimeout')} timeout")
return False
Expand All @@ -404,6 +446,9 @@ def waitForPageLoad(self):
while not self.isInjected:
if self.page.is_closed():
return
# Stop when close
if self.isClosed:
return
# TODO::
self.ThreadsafeBrowser.sleep(.2)

Expand All @@ -414,10 +459,14 @@ async def waitForPageLoad_(self):
while not self.isInjected:
if self.page.is_closed():
return
# Stop when close
if self.isClosed:
return
# TODO::
await asyncio.sleep(.2)

await self.ThreadsafeBrowser.page_wait_for_function("() => window.WPP.isReady", timeout=120 * 1000, page=self.page)
await self.ThreadsafeBrowser.page_wait_for_function("() => window.WPP.isReady", timeout=120 * 1000,
page=self.page)

async def waitForLogin_(self):
self.logger.info(f'{self.session}: http => Waiting page load')
Expand Down Expand Up @@ -477,7 +526,7 @@ async def waitForLogin_(self):
self.logger.error(f'{self.session}: Auto Close Called')
raise Exception("Auto Close Called")

if self.page.is_closed():
if self.page.is_closed() or self.isClosed:
self.logger.error(f'{self.session}: Page Closed')
raise Exception("Page Closed")

Expand Down Expand Up @@ -542,7 +591,7 @@ def waitForLogin(self):
self.logger.error(f'{self.session}: Auto Close Called')
raise Exception("Auto Close Called")

if self.page.is_closed():
if self.page.is_closed() or self.isClosed:
self.logger.error(f'{self.session}: Page Closed')
raise Exception("Page Closed")

Expand Down Expand Up @@ -592,7 +641,7 @@ def isMultiDevice(self):

async def isAuthenticated(self):
try:
if self.page.is_closed():
if self.page.is_closed() or self.isClosed:
return None
return await self.ThreadsafeBrowser.page_evaluate(
"() => typeof window.WPP !== 'undefined' && window.WPP.conn.isRegistered()", page=self.page)
Expand All @@ -602,7 +651,7 @@ async def isAuthenticated(self):

def sync_isAuthenticated(self):
try:
if self.page.is_closed():
if self.page.is_closed() or self.isClosed:
return False
return self.ThreadsafeBrowser.page_evaluate_sync(
"() => typeof window.WPP !== 'undefined' && window.WPP.conn.isRegistered()", page=self.page)
Expand Down Expand Up @@ -802,6 +851,7 @@ def valid_chatId(chatId):
return chatId

def close(self):
self.isClosed = True
self.ThreadsafeBrowser.sync_close()

@staticmethod
Expand Down
29 changes: 25 additions & 4 deletions WPP_Whatsapp/controllers/browser.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import asyncio

import playwright
import subprocess
from PlaywrightSafeThread.browser.threadsafe_browser import ThreadsafeBrowser as Tb, BrowserName, SUPPORTED_BROWSERS, \
Logger
Logger, creation_flags_dict, compute_driver_executable
from playwright.async_api import Error


Expand Down Expand Up @@ -57,7 +56,7 @@ async def wa(selector):
try:
await self.page.wait_for_selector(selector, timeout=timeout)
return selector
except :
except:
return

tasks = [self.loop.create_task(wa(selector)) for selector in selectors]
Expand All @@ -73,3 +72,25 @@ async def wa(selector):

return task_done.result()

def run_playwright(self, *args: str):
env = self.get_driver_env()
driver_executable, driver_cli = compute_driver_executable()

with subprocess.Popen([driver_executable, driver_cli, *args], env=env, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, **creation_flags_dict()) as process:
for line in process.stdout:
print(line.decode('utf-8'), end="\r")

def sync_close(self, timeout_=60):
try:
self.run_threadsafe(self.__stop_playwright(), timeout_=timeout_)
except Exception as e:
print(e)
self.stop()

async def close(self):
try:
await self.create_task(self.__stop_playwright())
except Exception as e:
print(e)
self.stop()
7 changes: 6 additions & 1 deletion WPP_Whatsapp/controllers/initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,18 @@ def __exit__(self, *args):
self.sync_close()

async def close(self):
if self.client:
self.client.isClosed = True
if hasattr(self, "ThreadsafeBrowser"):
await self.ThreadsafeBrowser.close()
self._onStateChange("CLOSED")

def sync_close(self):
if self.client:
self.client.isClosed = True
if hasattr(self, "ThreadsafeBrowser"):
self.ThreadsafeBrowser.sync_close()

self._onStateChange("CLOSED")

def _onStateChange(self, state):
Expand Down Expand Up @@ -139,7 +144,7 @@ async def start_(self) -> "Whatsapp":
await self.create()
elif self.state in ["CONFLICT", "UNPAIRED", "UNLAUNCHED"]:
Logger.info("client.useHere()")
self.client.useHere()
await self.client.useHere_()
else:
Logger.info(self.get_state())

Expand Down
1 change: 1 addition & 0 deletions examples/send_text_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
message = "hello from wpp"
phone_number = "***********" # or "+***********"

creator.sync_close()
# example
# Simple message
# result = client.sendText(phone_number, message)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"the creation of any interaction, such as customer service, media sending, intelligence recognition "
"based on phrases artificial and many other things, use your imagination")

version = "0.4.1"
version = "0.4.5"

setup(
name="WPP_Whatsapp",
Expand Down
57 changes: 29 additions & 28 deletions test/3.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
import logging
try:
from WPP_Whatsapp import Create
except (ModuleNotFoundError, ImportError):
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from WPP_Whatsapp import Create

from WPP_Whatsapp import Create
import logging

logger = logging.getLogger()
logger = logging.getLogger(name="WPP_Whatsapp")
logger.setLevel(logging.DEBUG)

def new_message(message):
global client
if message:
mensagem = message.get('body')
chat_id = message.get('from')

# Extract user ID and message content
usuario_id = message.get('chatId')['user']

print(usuario_id, mensagem)

if usuario_id == 'mynumber':
client.sendText(chat_id, "Here's what you said:" + mensagem)



# start client with your session name
your_session_name = "test"
creator = Create(session=your_session_name, browser='chrome')
creator = Create(session=your_session_name, browser="firefox")
client = creator.start()
# Now scan Whatsapp Qrcode in browser

# check state of login
if creator.state != 'CONNECTED':
raise Exception(creator.state)

print('Starting!')

client.sendText('***********0', 'Testando docker')

creator.client.onMessage(new_message)
# creator.loop.run_forever()
# while True:
# pass
message = "hello from wpp"
phone_number = "***********" # or "+***********"

creator.sync_close()
# example
# Simple message
# result = client.sendText(phone_number, message)
# print(result)
# client.forwardMessages("***********@c.us", 'true_***********@c.us_3EB07B4EABBAB75F0BD08A_out')
"""
sendText:
Sends a text message to given chat
@category Chat
@param to chat id: [email protected]
@param content text message
@option dict
return dict -> {'id': 'true_**********@c.us_*************_out', 'ack': 3, 'sendMsgResult': {}}
"""
Loading

0 comments on commit b9f5c81

Please sign in to comment.