From 99af4dfa51c06060e37f38f28726c283e38f46e7 Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Wed, 1 Jun 2022 16:18:53 +0200 Subject: [PATCH 01/11] try to send email after check_results --- check_results.py | 11 ++++++++++- notification/AcceptedResultPojo.py | 6 +++--- notification/mailer.py | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/check_results.py b/check_results.py index da5e89c..f423d79 100644 --- a/check_results.py +++ b/check_results.py @@ -8,6 +8,8 @@ from playwright.sync_api import sync_playwright import params from logs.LogSender import TYPE_EVENT_CHECK_RESULTS, LOG_SUBJECT_EVENT +from notification.AcceptedResultPojo import get_accepted_result_from +from notification.mailer import Mailer from pojo.ReserveResultPojo import ReserveResultPojo from pojo.ResultEnum import ResultEnum @@ -17,6 +19,8 @@ NOT_AVAILABLE_CONTENT = "For more than 130 years, our House has offered its full PENDING_SENTENCE = "Ce soir, entre 20:00 et 20:30, vous obtiendrez une réponse par e-mail." PENDING_SENTENCE_EN = "This evening between 20:00 and 20:30 you will receive a response by email." +mailer = Mailer() + class TlsPlaywright(threading.local): def __init__(self) -> None: @@ -100,6 +104,11 @@ class ResultChecker: else: print("status is ACCEPTED") status = ResultEnum.ACCEPTED + # send email + try: + mailer.send_email(get_accepted_result_from(reserve_pojo)) + except Exception as err: + print(err) collection.document(reserve_pojo.id).update({u'accepted': status.name}) @@ -116,7 +125,7 @@ if __name__ == '__main__': reserve_pojo = ReserveResultPojo.from_firestore_dict(appointment.to_dict()) result_list.append(reserve_pojo) - with ThreadPoolExecutor(max_workers=5) as executor: + with ThreadPoolExecutor(max_workers=10) as executor: for reserve in result_list: count = count + 1 if reserve.accepted is None or ResultEnum.ACCEPTED.value == reserve.accepted: diff --git a/notification/AcceptedResultPojo.py b/notification/AcceptedResultPojo.py index 7aafbb5..1490e8b 100644 --- a/notification/AcceptedResultPojo.py +++ b/notification/AcceptedResultPojo.py @@ -21,15 +21,15 @@ class AcceptedResultPojo: self.url = url -def get_accepted_result_from(sms, sim_info: ReserveResultPojo) -> AcceptedResultPojo: +def get_accepted_result_from(sim_info: ReserveResultPojo) -> AcceptedResultPojo: if sim_info is None: # send email even there are no reserve info - return AcceptedResultPojo(sms.text, slot_position=0, sim_position=0, + return AcceptedResultPojo("", slot_position=0, sim_position=0, passport="", email="", phone="", name="", ccid="", url="") else: - return AcceptedResultPojo(sms.text, slot_position=sim_info.slot_position, sim_position=sim_info.sim_position, + return AcceptedResultPojo("", slot_position=sim_info.slot_position, sim_position=sim_info.sim_position, passport=sim_info.passport, email=sim_info.email, phone=sim_info.phone, name="{} {}".format(sim_info.lastName, sim_info.firstName), ccid=sim_info.ccid, url=sim_info.url) diff --git a/notification/mailer.py b/notification/mailer.py index 6a57aa2..b6cf949 100644 --- a/notification/mailer.py +++ b/notification/mailer.py @@ -26,8 +26,8 @@ class Mailer: aws_secret_access_key=secret) def send_email(self, result: AcceptedResultPojo): - recipients = ['panleicim@gmail.com', 'kamenonly@gmail.com', 'tangliang0411@gmail.com'] - # recipients = ['panleicim@gmail.com'] + # recipients = ['panleicim@gmail.com', 'kamenonly@gmail.com', 'tangliang0411@gmail.com'] + recipients = ['panleicim@gmail.com'] mytemplate = Template(filename=definitions.ROOT_DIR + "/templates/appointment_results.html") self.logger.info("send email to " + str(recipients)) From 8e31c25527d9d90539022d25f57bf128f60612eb Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Wed, 1 Jun 2022 16:38:09 +0200 Subject: [PATCH 02/11] use proxy of brightdatao --- check_results.py | 15 +++++++-------- params.py | 10 ++++++++-- utils/new_profile_500.xlsx | Bin 27256 -> 0 bytes workers/commandor_page.py | 12 ++++++------ 4 files changed, 21 insertions(+), 16 deletions(-) delete mode 100644 utils/new_profile_500.xlsx diff --git a/check_results.py b/check_results.py index 39ad769..a826277 100644 --- a/check_results.py +++ b/check_results.py @@ -18,6 +18,7 @@ PENDING_SENTENCE = "Ce soir, entre 20:00 et 20:30, vous obtiendrez une réponse PENDING_SENTENCE_EN = "This evening between 20:00 and 20:30 you will receive a response by email." BLANK_URL = "about:blank" + class TlsPlaywright(threading.local): def __init__(self) -> None: self.playwright = sync_playwright().start() @@ -65,19 +66,17 @@ class ResultChecker: proxy_username = "panleicim-res-fr-" + random_id_number print("proxy_username is " + proxy_username) proxy = { - "server": params.PROXY_SERVER, - "username": proxy_username, - "password": params.PROXY_PASSWORD + "server": params.BRIGHT_DATA_PROXY_SERVER, + "username": params.BRIGHT_DATA_PROXY_USERNAME, + "password": params.BRIGHT_DATA_PROXY_PASSWORD } while content is None: content = self.load_page(self.tls.playwright, proxy, url) random_id_number = params.get_random_id_number_for_proxy() - proxy_username = "panleicim-res-fr-" + random_id_number - print("proxy_username is " + proxy_username) proxy = { - "server": params.PROXY_SERVER, - "username": proxy_username, - "password": params.PROXY_PASSWORD + "server": params.BRIGHT_DATA_PROXY_SERVER, + "username": params.BRIGHT_DATA_PROXY_USERNAME, + "password": params.BRIGHT_DATA_PROXY_PASSWORD } print(content) self.browser.close() diff --git a/params.py b/params.py index 0fc1bf0..dbea4df 100644 --- a/params.py +++ b/params.py @@ -13,15 +13,21 @@ oracle_log_sender = LogSender() # proxy PROXY_SERVER = "http://gw.ntnt.io:5959" PROXY_PASSWORD = "94sY7zwBG13i" + +BRIGHT_DATA_PROXY_SERVER = "http://zproxy.lum-superproxy.io:22225" +BRIGHT_DATA_PROXY_USERNAME = " lum-customer-c_daabba94-zone-residential-country-fr" +BRIGHT_DATA_PROXY_PASSWORD = "9dwmh54u3bbh" PROXY_NAME_PREFIX_RES = "panleicim-res-fr-" PROXY_NAME_PREFIX_CC = "panleicim-cc-fr-" -def get_proxy_name_prefix(proxy_type = 0) -> str: - if proxy_type ==0: + +def get_proxy_name_prefix(proxy_type=0) -> str: + if proxy_type == 0: return PROXY_NAME_PREFIX_RES else: return PROXY_NAME_PREFIX_CC + def get_random_id_number_for_proxy() -> str: S = 8 # number of characters in the string. ran = ''.join(random.choices(string.digits, k=S)) diff --git a/utils/new_profile_500.xlsx b/utils/new_profile_500.xlsx deleted file mode 100644 index 77bcbe78d34a0062ef19b21aa2ee8069240dbd1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27256 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 Date: Thu, 2 Jun 2022 09:47:29 +0200 Subject: [PATCH 03/11] use brightdata proxy --- check_results.py | 13 ++++++------- definitions.py | 16 ++++++++++++++++ main.py | 6 +++--- params.py | 2 +- utils/excel_reader.py | 12 +++++++++--- utils/name_generator.py | 19 +++++++++++++++++++ workers/commandor_page.py | 8 +++----- 7 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 utils/name_generator.py diff --git a/check_results.py b/check_results.py index 2e2ec2c..a1b7ca0 100644 --- a/check_results.py +++ b/check_results.py @@ -76,7 +76,6 @@ class ResultChecker: } while content is None: content = self.load_page(self.tls.playwright, proxy, url) - random_id_number = params.get_random_id_number_for_proxy() proxy = { "server": params.BRIGHT_DATA_PROXY_SERVER, "username": params.BRIGHT_DATA_PROXY_USERNAME, @@ -104,10 +103,10 @@ class ResultChecker: print("status is ACCEPTED") status = ResultEnum.ACCEPTED # send email - try: - mailer.send_email(get_accepted_result_from(reserve_pojo)) - except Exception as err: - print(err) + # try: + # mailer.send_email(get_accepted_result_from(reserve_pojo)) + # except Exception as err: + # print(err) collection.document(reserve_pojo.id).update({u'accepted': status.name}) @@ -124,10 +123,10 @@ if __name__ == '__main__': reserve_pojo = ReserveResultPojo.from_firestore_dict(appointment.to_dict()) result_list.append(reserve_pojo) - with ThreadPoolExecutor(max_workers=10) as executor: + with ThreadPoolExecutor(max_workers=20) as executor: for reserve in result_list: count = count + 1 - if reserve.accepted is None or ResultEnum.ACCEPTED.value == reserve.accepted: + if reserve.accepted is None or ResultEnum.PENDING.value == reserve.accepted: if reserve.url != BLANK_URL: executor.submit(ResultChecker().run, reserve, collection) else: diff --git a/definitions.py b/definitions.py index 006fdbb..0f7bbd6 100644 --- a/definitions.py +++ b/definitions.py @@ -3,6 +3,21 @@ import os import getpass from pathlib import Path +import sqlalchemy as sqlalchemy +from sqlalchemy_utils import create_database +from sqlalchemy_utils.functions import database_exists + + +def init_db(path: str): + uri_for_db = "sqlite:///{}/{}.db".format(path, "appointment") + print(uri_for_db) + # 2.-Turn on database engine + dbEngine = sqlalchemy.create_engine(uri_for_db) # ensure this is the correct path for the sqlite file. + if not database_exists(uri_for_db): + create_database(uri_for_db) + dbEngine.connect() + + home = str(Path.home()) config = configparser.ConfigParser() print("home path: " + home) @@ -15,4 +30,5 @@ LOGS_DIR = config['DEFAULT']['LOGS_DIR'] username = getpass.getuser() LOG_SOURCE = username +init_db(home) ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/main.py b/main.py index e05c530..1f1b0a9 100644 --- a/main.py +++ b/main.py @@ -38,9 +38,9 @@ def get_proxy(phone_number, proxy_type=0): proxy_username = params.get_proxy_name_prefix(proxy_type) + random_id_number logger.info("proxy_username is " + proxy_username) proxy = { - "server": params.PROXY_SERVER, - "username": proxy_username, - "password": params.PROXY_PASSWORD + "server": params.BRIGHT_DATA_PROXY_SERVER, + "username": params.BRIGHT_DATA_PROXY_USERNAME, + "password": params.BRIGHT_DATA_PROXY_PASSWORD } return proxy diff --git a/params.py b/params.py index b1a9776..50ebce8 100644 --- a/params.py +++ b/params.py @@ -12,7 +12,7 @@ PROXY_SERVER = "http://gw.ntnt.io:5959" PROXY_PASSWORD = "94sY7zwBG13i" BRIGHT_DATA_PROXY_SERVER = "http://zproxy.lum-superproxy.io:22225" -BRIGHT_DATA_PROXY_USERNAME = " lum-customer-c_daabba94-zone-residential-country-fr" +BRIGHT_DATA_PROXY_USERNAME = "lum-customer-c_daabba94-zone-residential-country-fr" BRIGHT_DATA_PROXY_PASSWORD = "9dwmh54u3bbh" PROXY_NAME_PREFIX_RES = "panleicim-res-fr-" PROXY_NAME_PREFIX_CC = "panleicim-cc-fr-" diff --git a/utils/excel_reader.py b/utils/excel_reader.py index 04a8281..c95ecc7 100644 --- a/utils/excel_reader.py +++ b/utils/excel_reader.py @@ -54,6 +54,8 @@ class ExcelHelper: name = raw_name.split(' ') if len(name) == 1: name = raw_name.split('\xa0') + if len(name) == 1: + print("error in " + str(name)) last_name = name[0] if len(name) == 2: first_name = name[-1] @@ -85,10 +87,14 @@ def get_random_phone_numbers(): def generate_email_from_name(first_name: str, last_name: str) -> str: - length = 4 # number of characters in the string. + length = 2 # number of characters in the string. ran = ''.join(random.choices(string.digits, k=length)) separator = ['.', '_', ''] - email = "{}{}{}{}@163.com".format(last_name.lower(), random.choice(separator), first_name.lower(), ran) + domains = ['gmail.com', 'hotmail.com', 'yahoo.com', 'aol.com', 'outlook.com', 'hotmail.fr', 'gmx.com', + 'hotmail.com', 'yahoo.com', 'aol.com', 'hotmail.com'] + email = "{}{}{}{}@{}".format(last_name.lower(), random.choice(separator), + first_name.replace("-", "").replace("'", "").lower(), ran, + random.choice(domains)) print(email) return email @@ -129,6 +135,6 @@ def write_new_contacts_to_excel(valid_contacts: list): if __name__ == '__main__': excel_reader = ExcelHelper() - contacts = excel_reader.read_names("C:/Users/landd/Desktop/1000.xlsx") + contacts = excel_reader.read_names("C:/Users/landd/Downloads/names.xlsx") print(contacts) write_new_contacts_to_excel(valid_contacts=contacts) diff --git a/utils/name_generator.py b/utils/name_generator.py new file mode 100644 index 0000000..c83167d --- /dev/null +++ b/utils/name_generator.py @@ -0,0 +1,19 @@ +import json + +import requests + + +def generate_names() -> list: + res = requests.get( + "https://www.namegeneratorfun.com/api/namegenerator?generatorType=list&firstName=&lastName=&minLength=0&maxLength=255&sexId=2&generatorId=43") + response_json = json.loads(res.text) + names = response_json['names'] + # print(names) + return names + + +if __name__ == '__main__': + for i in range(0, 5): + names = generate_names() + for name in names: + print(name) diff --git a/workers/commandor_page.py b/workers/commandor_page.py index 4422d08..e6a80a9 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -92,12 +92,10 @@ class CommandorPage: first_page = None while first_page is None: first_page = self.start_browser(proxy, self.tls.playwright, devices) - proxy_username = params.get_proxy_name_prefix(self.proxy_type) + params.get_random_id_number_for_proxy() - self.logger.info("proxy_username is " + proxy_username) proxy = { - "server": params.PROXY_SERVER, - "username": proxy_username, - "password": params.PROXY_PASSWORD + "server": params.BRIGHT_DATA_PROXY_SERVER, + "username": params.BRIGHT_DATA_PROXY_USERNAME, + "password": params.BRIGHT_DATA_PROXY_PASSWORD } # wait for sms_code field # self.clickOnValidBtn() From f3694ef8ee02c5a68ba45d12453888ab5890deff Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Thu, 2 Jun 2022 15:04:49 +0200 Subject: [PATCH 04/11] reset the correct url --- workers/commandor_page.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workers/commandor_page.py b/workers/commandor_page.py index e6a80a9..3f26a3b 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -15,10 +15,10 @@ from pojo.ReserveResultPojo import ReserveResultPojo, PublishType from pojo.contact_pojo import ContactPojo from workers.SolveCaptch import SolveCaptcha -# RDV_URL = "https://rendezvousparis.hermes.com/client/register" +RDV_URL = "https://rendezvousparis.hermes.com/client/register" # RDV_URL = "file:///Users/lpan/Downloads/test_appointment.html" -RDV_URL = "https://api.ipify.org" +# RDV_URL = "https://api.ipify.org" # RDV_URL ="https://bot.sannysoft.com/" REGEX_RDV_URL = "https:\/\/rendezvousparis\.hermes\.com\/client\/register\/[A-Z0-9]+" otp_value = None From d13a96b984e97be9c2890f421ef4299552cea4cd Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Thu, 2 Jun 2022 17:59:12 +0200 Subject: [PATCH 05/11] save errors in local db --- db/local_db_manager.py | 44 ++++++++++++++++++++++++++++++ definitions.py | 21 +++----------- pojo/captcha_error_contact_pojo.py | 43 +++++++++++++++++++++++++++++ requirements.txt | 5 +++- workers/commandor_page.py | 5 ++++ 5 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 db/local_db_manager.py create mode 100644 pojo/captcha_error_contact_pojo.py diff --git a/db/local_db_manager.py b/db/local_db_manager.py new file mode 100644 index 0000000..0f3d768 --- /dev/null +++ b/db/local_db_manager.py @@ -0,0 +1,44 @@ +import sqlalchemy +from sqlalchemy import MetaData, Column, String, Integer, DateTime, Table +from sqlalchemy.orm import Session +from sqlalchemy_utils import database_exists, create_database + +from pojo.captcha_error_contact_pojo import ContactInErrorPojo + + +class LocalDbManager: + + def __init__(self, path: str): + self.session = Session(self.init_db(path)) + + def init_db(self, path: str): + uri_for_db = "sqlite:///{}/{}.db".format(path, "appointment") + print(uri_for_db) + # 2.-Turn on database engine + db_engine = sqlalchemy.create_engine(uri_for_db) # ensure this is the correct path for the sqlite file. + if not database_exists(uri_for_db): + create_database(uri_for_db) + connextion = db_engine.connect() + if not db_engine.dialect.has_table(connextion, + ContactInErrorPojo.__tablename__): # If table don't exist, Create. + metadata = MetaData(db_engine) + # Create a table with the appropriate Columns + Table(ContactInErrorPojo.__tablename__, metadata, + Column('mail', String, primary_key=True, nullable=False), + Column('phone', String), + Column('passport', String), + Column('last_name', String), + Column('first_name', String), + Column('ccid', String), + Column('position', Integer), + Column('error_type', Integer), + Column('update_at', DateTime), + Column('create_at', DateTime)) + # Implement the creation + metadata.create_all() + + return db_engine + + def insert_or_update(self, instance: ContactInErrorPojo): + self.session.merge(instance) + self.session.commit() diff --git a/definitions.py b/definitions.py index 0f7bbd6..d6a91f3 100644 --- a/definitions.py +++ b/definitions.py @@ -1,26 +1,14 @@ import configparser -import os import getpass +import os from pathlib import Path -import sqlalchemy as sqlalchemy -from sqlalchemy_utils import create_database -from sqlalchemy_utils.functions import database_exists - - -def init_db(path: str): - uri_for_db = "sqlite:///{}/{}.db".format(path, "appointment") - print(uri_for_db) - # 2.-Turn on database engine - dbEngine = sqlalchemy.create_engine(uri_for_db) # ensure this is the correct path for the sqlite file. - if not database_exists(uri_for_db): - create_database(uri_for_db) - dbEngine.connect() - +from db.local_db_manager import LocalDbManager home = str(Path.home()) config = configparser.ConfigParser() print("home path: " + home) + # check the config file exsistence config_file_path = home + "/config.ini" config.read(config_file_path) @@ -29,6 +17,5 @@ FIREBASE_CONFIG_FILE = config['DEFAULT']['firebase_config_file'] LOGS_DIR = config['DEFAULT']['LOGS_DIR'] username = getpass.getuser() LOG_SOURCE = username - -init_db(home) +local_db_manager = LocalDbManager(home) ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/pojo/captcha_error_contact_pojo.py b/pojo/captcha_error_contact_pojo.py new file mode 100644 index 0000000..4afe5ac --- /dev/null +++ b/pojo/captcha_error_contact_pojo.py @@ -0,0 +1,43 @@ +from sqlalchemy import Column, String, Integer, DateTime, func +from sqlalchemy.orm import declarative_base + +import definitions +from pojo.contact_pojo import ContactPojo + +Base = declarative_base() + +ERROR_TYPE_CAPTCHA = 1 + + +class ContactInErrorPojo(Base): + __tablename__ = "contacts_in_error" + + mail: str = Column(String, primary_key=True) + phone: str = Column(String) + passport: str = Column(String) + last_name: str = Column(String) + first_name: str = Column(String) + ccid: str = Column(String) + position: int = Column(Integer) + error_type = Column(Integer) + update_at = Column(DateTime, onupdate=func.now()) + create_at = Column(DateTime, default=func.now()) + + +def get_captcha_error_contact_from_contact(contact: ContactPojo, error_type: int) -> ContactInErrorPojo: + captcha_error = ContactInErrorPojo() + captcha_error.mail = contact.mail + captcha_error.ccid = contact.ccid + captcha_error.phone = contact.phone + captcha_error.passport = contact.passport + captcha_error.first_name = contact.first_name + captcha_error.last_name = contact.last_name + captcha_error.position = contact.position + captcha_error.error_type = error_type + return captcha_error + + +if __name__ == '__main__': + conact = ContactPojo(mail="panleici3m@gmail.com", phone_number="649114592", ccid="", position=0, + passport_number="3322111", first_name="Lei", last_name="PAAaN") + definitions.local_db_manager.insert_or_update(get_captcha_error_contact_from_contact(conact, ERROR_TYPE_CAPTCHA)) diff --git a/requirements.txt b/requirements.txt index da21495..5d1b9ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,7 @@ boto3~=1.21.13 openpyxl==3.0.9 google-cloud-firestore==2.4.0 PySimpleGUI==4.60.0 -SpeechRecognition==3.8.1 \ No newline at end of file +SpeechRecognition==3.8.1 +SQLAlchemy~=1.4.37 +requests~=2.27.1 +Mako~=1.2.0 \ No newline at end of file diff --git a/workers/commandor_page.py b/workers/commandor_page.py index 3f26a3b..7bfb264 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -8,10 +8,12 @@ from typing import Union from playwright.sync_api import sync_playwright +import definitions import params from params import PROXY_SERVER, PROXY_PASSWORD from pojo.ModeEnum import ModeEnum from pojo.ReserveResultPojo import ReserveResultPojo, PublishType +from pojo.captcha_error_contact_pojo import get_captcha_error_contact_from_contact, ERROR_TYPE_CAPTCHA from pojo.contact_pojo import ContactPojo from workers.SolveCaptch import SolveCaptcha @@ -256,6 +258,9 @@ class CommandorPage: # this email has been already used self.is_captcha_in_error = True if not self.is_finished: + # save the error to database with contact info + definitions.local_db_manager.insert_or_update( + get_captcha_error_contact_from_contact(self.contact, ERROR_TYPE_CAPTCHA)) params.oracle_log_sender.send_captcha_error(self.contact) self.is_finished = True # no need to retry captcha, if retry ,will generate DOUBLE_REQUEST_ERROR_MESSAGE From e925945300f455b7ae0d5100954f6f27959708c3 Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Fri, 3 Jun 2022 11:51:59 +0200 Subject: [PATCH 06/11] change error schema --- check_results.py | 11 ++++++----- db/local_db_manager.py | 3 ++- pojo/captcha_error_contact_pojo.py | 11 ++++++----- workers/commandor_page.py | 11 ++++------- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/check_results.py b/check_results.py index 742c549..43a5f2c 100644 --- a/check_results.py +++ b/check_results.py @@ -8,6 +8,7 @@ from playwright.sync_api import sync_playwright import params from logs.LogSender import TYPE_EVENT_CHECK_RESULTS, LOG_SUBJECT_EVENT +from notification.AcceptedResultPojo import get_accepted_result_from from notification.mailer import Mailer from pojo.ReserveResultPojo import ReserveResultPojo from pojo.ResultEnum import ResultEnum @@ -101,11 +102,11 @@ class ResultChecker: else: print("status is ACCEPTED") status = ResultEnum.ACCEPTED - # send email - # try: - # mailer.send_email(get_accepted_result_from(reserve_pojo)) - # except Exception as err: - # print(err) + # send email + try: + mailer.send_email(get_accepted_result_from(reserve_pojo)) + except Exception as err: + print(err) collection.document(reserve_pojo.id).update({u'accepted': status.name}) diff --git a/db/local_db_manager.py b/db/local_db_manager.py index 0f3d768..25c075d 100644 --- a/db/local_db_manager.py +++ b/db/local_db_manager.py @@ -24,7 +24,8 @@ class LocalDbManager: metadata = MetaData(db_engine) # Create a table with the appropriate Columns Table(ContactInErrorPojo.__tablename__, metadata, - Column('mail', String, primary_key=True, nullable=False), + Column('id', Integer, primary_key=True, autoincrement=True), + Column('mail', String), Column('phone', String), Column('passport', String), Column('last_name', String), diff --git a/pojo/captcha_error_contact_pojo.py b/pojo/captcha_error_contact_pojo.py index 4afe5ac..e11f5c9 100644 --- a/pojo/captcha_error_contact_pojo.py +++ b/pojo/captcha_error_contact_pojo.py @@ -7,12 +7,13 @@ from pojo.contact_pojo import ContactPojo Base = declarative_base() ERROR_TYPE_CAPTCHA = 1 +TOO_MANY_REQUEST_ERROR = 2 class ContactInErrorPojo(Base): __tablename__ = "contacts_in_error" - - mail: str = Column(String, primary_key=True) + id = Column(Integer, primary_key=True, autoincrement=True) + mail: str = Column(String) phone: str = Column(String) passport: str = Column(String) last_name: str = Column(String) @@ -38,6 +39,6 @@ def get_captcha_error_contact_from_contact(contact: ContactPojo, error_type: int if __name__ == '__main__': - conact = ContactPojo(mail="panleici3m@gmail.com", phone_number="649114592", ccid="", position=0, - passport_number="3322111", first_name="Lei", last_name="PAAaN") - definitions.local_db_manager.insert_or_update(get_captcha_error_contact_from_contact(conact, ERROR_TYPE_CAPTCHA)) + contact = ContactPojo(mail="panleici3m@gmail.com", phone_number="649114592", ccid="", position=0, + passport_number="3322111", first_name="Lei", last_name="PAAaN") + definitions.local_db_manager.insert_or_update(get_captcha_error_contact_from_contact(contact, ERROR_TYPE_CAPTCHA)) diff --git a/workers/commandor_page.py b/workers/commandor_page.py index 8f8d0dd..01294c5 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -12,7 +12,8 @@ import definitions import params from pojo.ModeEnum import ModeEnum from pojo.ReserveResultPojo import ReserveResultPojo, PublishType -from pojo.captcha_error_contact_pojo import get_captcha_error_contact_from_contact, ERROR_TYPE_CAPTCHA +from pojo.captcha_error_contact_pojo import get_captcha_error_contact_from_contact, ERROR_TYPE_CAPTCHA, \ + TOO_MANY_REQUEST_ERROR from pojo.contact_pojo import ContactPojo from workers.SolveCaptch import SolveCaptcha @@ -250,6 +251,8 @@ class CommandorPage: elif TOO_MANY_REQUEST_ERROR_MESSAGE in erro_content: # this email has been already used if not self.is_finished: + definitions.local_db_manager.insert_or_update( + get_captcha_error_contact_from_contact(self.contact, TOO_MANY_REQUEST_ERROR)) params.oracle_log_sender.send_too_many_error(self.contact) self.is_finished = True self.termine() @@ -306,9 +309,6 @@ class CommandorPage: except Exception as error: self.logger.error(error) - def clear_app_data(self): - pass - def fill_otp(self, otp: str): self.page.focus(OTP_FIELD_ID) time.sleep(get_random_wait_time()) @@ -369,15 +369,12 @@ def on_success(result: ReserveResultPojo): def launch_page(): - PROXY_USERNAME = "panleicim-res-fr-" + params.get_random_id_number_for_proxy() - print("proxy_username is " + PROXY_USERNAME) proxy = { "server": params.BRIGHT_DATA_PROXY_SERVER, "username": params.BRIGHT_DATA_PROXY_USERNAME, "password": params.BRIGHT_DATA_PROXY_PASSWORD } passport_number = get_random_id_number() - print("passport_number is " + passport_number) contact = ContactPojo(phone_number="+33758912245", passport_number=passport_number, last_name="XU", first_name="xingzhen", mail="ColbyPatel653@gmail.com", ccid="", position=0) From 64ff15bf4db1365241651d96230d863bcd4110f8 Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Sat, 4 Jun 2022 09:57:08 +0200 Subject: [PATCH 07/11] local changes for check results --- check_results.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/check_results.py b/check_results.py index 43a5f2c..012aa7f 100644 --- a/check_results.py +++ b/check_results.py @@ -123,10 +123,10 @@ if __name__ == '__main__': reserve_pojo = ReserveResultPojo.from_firestore_dict(appointment.to_dict()) result_list.append(reserve_pojo) - with ThreadPoolExecutor(max_workers=25) as executor: + with ThreadPoolExecutor(max_workers=10) as executor: for reserve in result_list: count = count + 1 - if reserve.accepted is None or ResultEnum.PENDING.value == reserve.accepted: + if reserve.accepted is None or ResultEnum.ACCEPTED.value == reserve.accepted: if reserve.url != BLANK_URL: executor.submit(ResultChecker().run, reserve, collection) else: From 20c84b2a14b92ede91443851f64b12d9964a74e4 Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Wed, 8 Jun 2022 21:39:45 +0200 Subject: [PATCH 08/11] optimize local db --- check_results.py | 7 ++---- db/local_db_manager.py | 22 +++++++++++++++++-- definitions.py | 4 ---- main.py | 32 +++++++++++++++++----------- params.py | 22 +++++++++++++++++++ pojo/captcha_error_contact_pojo.py | 23 +------------------- pojo/contact_pojo.py | 8 +++++++ utils/generate_random_passport_id.py | 15 +++++++++++++ workers/commandor_page.py | 18 +++++++--------- 9 files changed, 95 insertions(+), 56 deletions(-) diff --git a/check_results.py b/check_results.py index 8d15d92..0bcdd26 100644 --- a/check_results.py +++ b/check_results.py @@ -41,7 +41,7 @@ class ResultChecker: device = random.choice(params.DEVICES) self.logger.info("模拟设备: " + device) pixel_2 = self.tls.playwright.devices[device] - context = self.browser.new_context(**pixel_2, locale='en-GB') + context = self.browser.new_context(**pixel_2, locale='fr-FR') self.page = context.new_page() # hide webdriver information self.page.add_init_script("""() => { @@ -66,9 +66,6 @@ class ResultChecker: # url_to_check = url + "?lang=fr" print("url is " + url) content = None - random_id_number = str(phone_number)[1:len(str(phone_number))] - proxy_username = "panleicim-res-fr-" + random_id_number - print("proxy_username is " + proxy_username) proxy = { "server": params.BRIGHT_DATA_PROXY_SERVER, "username": params.BRIGHT_DATA_PROXY_USERNAME, @@ -123,7 +120,7 @@ if __name__ == '__main__': reserve_pojo = ReserveResultPojo.from_firestore_dict(appointment.to_dict()) result_list.append(reserve_pojo) - with ThreadPoolExecutor(max_workers=10) as executor: + with ThreadPoolExecutor(max_workers=15) as executor: for reserve in result_list: count = count + 1 if reserve.accepted is None or ResultEnum.ACCEPTED.value == reserve.accepted: diff --git a/db/local_db_manager.py b/db/local_db_manager.py index 25c075d..be79267 100644 --- a/db/local_db_manager.py +++ b/db/local_db_manager.py @@ -1,9 +1,14 @@ +from builtins import list + import sqlalchemy from sqlalchemy import MetaData, Column, String, Integer, DateTime, Table from sqlalchemy.orm import Session from sqlalchemy_utils import database_exists, create_database -from pojo.captcha_error_contact_pojo import ContactInErrorPojo +import definitions +import params +from pojo.ReserveResultPojo import ReserveResultPojo +from pojo.captcha_error_contact_pojo import ContactInErrorPojo, ERROR_TYPE_CAPTCHA class LocalDbManager: @@ -12,7 +17,7 @@ class LocalDbManager: self.session = Session(self.init_db(path)) def init_db(self, path: str): - uri_for_db = "sqlite:///{}/{}.db".format(path, "appointment") + uri_for_db = "sqlite:///{}/{}.db?check_same_thread=false".format(path, "appointment") print(uri_for_db) # 2.-Turn on database engine db_engine = sqlalchemy.create_engine(uri_for_db) # ensure this is the correct path for the sqlite file. @@ -43,3 +48,16 @@ class LocalDbManager: def insert_or_update(self, instance: ContactInErrorPojo): self.session.merge(instance) self.session.commit() + + def get_all_captcha_error_contacts(self) -> list: + return self.session.query(ContactInErrorPojo).filter(ContactInErrorPojo.error_type == ERROR_TYPE_CAPTCHA).all() + + def handle_success(self, reservePojo: ReserveResultPojo): + # delete the contact from table + self.session.query(ContactInErrorPojo).filter(ContactInErrorPojo.mail == reservePojo.email).delete() + + +if __name__ == '__main__': + list = params.local_db_manager.get_all_captcha_error_contacts() + for item in list: + print(item.mail) diff --git a/definitions.py b/definitions.py index d6a91f3..447ef16 100644 --- a/definitions.py +++ b/definitions.py @@ -2,9 +2,6 @@ import configparser import getpass import os from pathlib import Path - -from db.local_db_manager import LocalDbManager - home = str(Path.home()) config = configparser.ConfigParser() print("home path: " + home) @@ -17,5 +14,4 @@ FIREBASE_CONFIG_FILE = config['DEFAULT']['firebase_config_file'] LOGS_DIR = config['DEFAULT']['LOGS_DIR'] username = getpass.getuser() LOG_SOURCE = username -local_db_manager = LocalDbManager(home) ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) diff --git a/main.py b/main.py index 1f1b0a9..f36a574 100644 --- a/main.py +++ b/main.py @@ -5,6 +5,7 @@ from concurrent.futures import ThreadPoolExecutor import params from logs.AppLogging import init_logger from pojo.ModeEnum import ModeEnum +from pojo.contact_pojo import ContactPojo from utils.excel_reader import ExcelHelper from workers.commandor_page import CommandorPage @@ -25,26 +26,31 @@ def start_book(start_number, end_number, store_choose_state=0, max_workers=10, p logger.info(contacts) with ThreadPoolExecutor(max_workers=max_workers) as executor: for contact in contacts: - proxy = get_proxy(contact.phone, proxy_type) + proxy = get_proxy(proxy_type) # start the task in thread executor.submit( CommandorPage(contact, store_type=store_choose_state, proxy_type=proxy_type, mode=mode).start_page, proxy) -def get_proxy(phone_number, proxy_type=0): - # random_id_number = str(phone_number)[1:len(str(phone_number))] - random_id_number = params.get_random_id_number_for_proxy() - proxy_username = params.get_proxy_name_prefix(proxy_type) + random_id_number - logger.info("proxy_username is " + proxy_username) - proxy = { - "server": params.BRIGHT_DATA_PROXY_SERVER, - "username": params.BRIGHT_DATA_PROXY_USERNAME, - "password": params.BRIGHT_DATA_PROXY_PASSWORD - } - return proxy +def recheck_the_captcha_error_contacts(store_type=0, mode: ModeEnum = ModeEnum.MANUAL): + # get all the contacts in captcha error + list = params.local_db_manager.get_all_captcha_error_contacts() + with ThreadPoolExecutor(max_workers=10) as executor: + for errorContact in list: + contact = ContactPojo.get_contact_from_error_contact(errorContact) + proxy = get_proxy() + # start the task in thread + executor.submit( + CommandorPage(contact, store_type=store_type, mode=mode).start_page, + proxy) + + +def get_proxy(proxy_type=0): + return params.get_proxy(proxy_type) if __name__ == '__main__': # 修改联系人行,结束联系人行 第三个参数store等于0的时候是随机,传入1的时候是总店 - start_book(16, 16, store_choose_state=0, proxy_type=0) + # start_book(16, 16, store_choose_state=0, proxy_type=0) + recheck_the_captcha_error_contacts(0, mode=ModeEnum.AUTOMATIC) diff --git a/params.py b/params.py index 50ebce8..c717864 100644 --- a/params.py +++ b/params.py @@ -1,7 +1,9 @@ import random import string +import definitions from db.DbManager import DataManager +from db.local_db_manager import LocalDbManager from logs.LogSender import LogSender firebase_store_manager = DataManager() @@ -25,6 +27,24 @@ def get_proxy_name_prefix(proxy_type=0) -> str: return PROXY_NAME_PREFIX_CC +def get_proxy(proxy_type=0): + if proxy_type == 0: + random_id_number = get_random_id_number_for_proxy() + proxy_username = get_proxy_name_prefix(proxy_type) + random_id_number + proxy = { + "server": PROXY_SERVER, + "username": proxy_username, + "password": PROXY_PASSWORD + } + else: + proxy = { + "server": BRIGHT_DATA_PROXY_SERVER, + "username": BRIGHT_DATA_PROXY_USERNAME, + "password": BRIGHT_DATA_PROXY_PASSWORD + } + return proxy + + def get_random_id_number_for_proxy() -> str: S = 8 # number of characters in the string. ran = ''.join(random.choices(string.digits, k=S)) @@ -33,6 +53,8 @@ def get_random_id_number_for_proxy() -> str: return id_number +local_db_manager = LocalDbManager(definitions.home) + DEVICES = ['iPad (gen 6)', 'iPad (gen 6) landscape', 'iPad (gen 7)', 'iPad (gen 7) landscape', 'iPad Mini', 'iPad Mini landscape', 'iPad Pro 11', 'iPad Pro 11 landscape', 'iPhone 6', 'iPhone 6 landscape', 'iPhone 6 Plus', 'iPhone 6 Plus landscape', 'iPhone 7', 'iPhone 7 landscape', 'iPhone 7 Plus', diff --git a/pojo/captcha_error_contact_pojo.py b/pojo/captcha_error_contact_pojo.py index e11f5c9..1b155d8 100644 --- a/pojo/captcha_error_contact_pojo.py +++ b/pojo/captcha_error_contact_pojo.py @@ -1,8 +1,6 @@ from sqlalchemy import Column, String, Integer, DateTime, func from sqlalchemy.orm import declarative_base -import definitions -from pojo.contact_pojo import ContactPojo Base = declarative_base() @@ -22,23 +20,4 @@ class ContactInErrorPojo(Base): position: int = Column(Integer) error_type = Column(Integer) update_at = Column(DateTime, onupdate=func.now()) - create_at = Column(DateTime, default=func.now()) - - -def get_captcha_error_contact_from_contact(contact: ContactPojo, error_type: int) -> ContactInErrorPojo: - captcha_error = ContactInErrorPojo() - captcha_error.mail = contact.mail - captcha_error.ccid = contact.ccid - captcha_error.phone = contact.phone - captcha_error.passport = contact.passport - captcha_error.first_name = contact.first_name - captcha_error.last_name = contact.last_name - captcha_error.position = contact.position - captcha_error.error_type = error_type - return captcha_error - - -if __name__ == '__main__': - contact = ContactPojo(mail="panleici3m@gmail.com", phone_number="649114592", ccid="", position=0, - passport_number="3322111", first_name="Lei", last_name="PAAaN") - definitions.local_db_manager.insert_or_update(get_captcha_error_contact_from_contact(contact, ERROR_TYPE_CAPTCHA)) + create_at = Column(DateTime, default=func.now()) \ No newline at end of file diff --git a/pojo/contact_pojo.py b/pojo/contact_pojo.py index 77ef414..de491c8 100644 --- a/pojo/contact_pojo.py +++ b/pojo/contact_pojo.py @@ -1,5 +1,7 @@ from dataclasses import dataclass +from pojo.captcha_error_contact_pojo import ContactInErrorPojo + @dataclass class ContactPojo: @@ -35,6 +37,12 @@ class ContactPojo: return dest + @staticmethod + def get_contact_from_error_contact(errorContact: ContactInErrorPojo): + return ContactPojo(phone_number=errorContact.phone, mail=errorContact.mail, ccid=errorContact.ccid, + last_name=errorContact.last_name, first_name=errorContact.first_name, + position=errorContact.position, passport_number=errorContact.passport) + @staticmethod def from_firestore_dict(source): ccid = source['ccid'] diff --git a/utils/generate_random_passport_id.py b/utils/generate_random_passport_id.py index a892600..66f5d2c 100644 --- a/utils/generate_random_passport_id.py +++ b/utils/generate_random_passport_id.py @@ -1,6 +1,9 @@ import random import string +from pojo.captcha_error_contact_pojo import ContactInErrorPojo +from pojo.contact_pojo import ContactPojo + letters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'P', 'Q', 'R', 'S', 'T', '1', '2', '3', '4', '5', '6', '7', '8', '9'] @@ -19,6 +22,18 @@ def get_random_passport_id_number() -> str: return id_number +def get_captcha_error_contact_from_contact(contact: ContactPojo, error_type: int) -> ContactInErrorPojo: + captcha_error = ContactInErrorPojo() + captcha_error.mail = contact.mail + captcha_error.ccid = contact.ccid + captcha_error.phone = contact.phone + captcha_error.passport = contact.passport + captcha_error.first_name = contact.first_name + captcha_error.last_name = contact.last_name + captcha_error.position = contact.position + captcha_error.error_type = error_type + return captcha_error + if __name__ == '__main__': # for i in range(1,200): # print(get_random_id_number()) diff --git a/workers/commandor_page.py b/workers/commandor_page.py index d206aa7..ac7f9f8 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -12,9 +12,10 @@ import definitions import params from pojo.ModeEnum import ModeEnum from pojo.ReserveResultPojo import ReserveResultPojo, PublishType -from pojo.captcha_error_contact_pojo import get_captcha_error_contact_from_contact, ERROR_TYPE_CAPTCHA, \ +from pojo.captcha_error_contact_pojo import ERROR_TYPE_CAPTCHA, \ TOO_MANY_REQUEST_ERROR from pojo.contact_pojo import ContactPojo +from utils.generate_random_passport_id import get_captcha_error_contact_from_contact from workers.SolveCaptch import SolveCaptcha RDV_URL = "https://rendezvousparis.hermes.com/client/register" @@ -32,7 +33,7 @@ CONFIRMED_MESSAGE_FR = "Votre demande de rendez-vous Maroquinerie a bien été e DOUBLE_REQUEST_ERROR_MESSAGE = "A request with the same data has already been validated today." DOUBLE_REQUEST_ERROR_MESSAGE_FR = "Une demande avec les données saisies a déjà été validée aujourd’hui." TOO_MANY_REQUEST_ERROR_MESSAGE = "Due to a large number of requests" -TOO_MANY_REQUEST_ERROR_MESSAGE_FR = "Suite à un trop grand nombre de demandes aujourd’hui," +TOO_MANY_REQUEST_ERROR_MESSAGE_FR = "Suite à un trop grand nombre de demandes" CAPTCHA_ERROR_MESSAGE = "Error verifying captcha, please try again" CAPTCHA_ERROR_MESSAGE_FR = "La vérification du captcha a échoué" TIME_OUT = 400000 @@ -98,11 +99,7 @@ class CommandorPage: first_page = None while first_page is None: first_page = self.start_browser(proxy, self.tls.playwright, devices) - proxy = { - "server": params.BRIGHT_DATA_PROXY_SERVER, - "username": params.BRIGHT_DATA_PROXY_USERNAME, - "password": params.BRIGHT_DATA_PROXY_PASSWORD - } + proxy = params.get_proxy(self.proxy_type) # wait for sms_code field # self.clickOnValidBtn() self.thread_event = e @@ -132,7 +129,7 @@ class CommandorPage: self.logger.info("填充信息: " + str(self.contact.phone)) self._set_name(self.contact.last_name, self.contact.first_name) self._setPhoneCountryAndStore() - self._setPhoneNumber(self.contact.phone) + self._setPhoneNumber("0" + str(self.contact.phone)) self._set_email(self.contact.mail) self.setIdNumber(self.contact.passport) self._checkCgu() @@ -255,7 +252,7 @@ class CommandorPage: elif TOO_MANY_REQUEST_ERROR_MESSAGE in erro_content or TOO_MANY_REQUEST_ERROR_MESSAGE_FR in erro_content: # this email has been already used if not self.is_finished: - definitions.local_db_manager.insert_or_update( + params.local_db_manager.insert_or_update( get_captcha_error_contact_from_contact(self.contact, TOO_MANY_REQUEST_ERROR)) params.oracle_log_sender.send_too_many_error(self.contact) self.is_finished = True @@ -265,7 +262,7 @@ class CommandorPage: self.is_captcha_in_error = True if not self.is_finished: # save the error to database with contact info - definitions.local_db_manager.insert_or_update( + params.local_db_manager.insert_or_update( get_captcha_error_contact_from_contact(self.contact, ERROR_TYPE_CAPTCHA)) params.oracle_log_sender.send_captcha_error(self.contact) self.is_finished = True @@ -331,6 +328,7 @@ class CommandorPage: passport=contact.passport, ccid=contact.ccid) result.id = id params.firebase_store_manager.save(result) + params.local_db_manager.handle_success(result) if status is PublishType.SUCCESS: self.on_success(result) time.sleep(2) From d268b02e4eb2f92dc8df70ea2bb29d44d5c30357 Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Thu, 9 Jun 2022 10:12:43 +0200 Subject: [PATCH 09/11] add mobile proxy --- check_results.py | 1 - params.py | 11 +++++++++-- workers/commandor_page.py | 8 ++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/check_results.py b/check_results.py index 0bcdd26..b81d7cf 100644 --- a/check_results.py +++ b/check_results.py @@ -61,7 +61,6 @@ class ResultChecker: def run(self, reserve_pojo: ReserveResultPojo, collection): print("Launched worker in ", threading.current_thread().name) url = reserve_pojo.url - phone_number = reserve_pojo.phone # url_to_check = url.replace("register/", "") # url_to_check = url + "?lang=fr" print("url is " + url) diff --git a/params.py b/params.py index c717864..126eb0e 100644 --- a/params.py +++ b/params.py @@ -15,7 +15,9 @@ PROXY_PASSWORD = "94sY7zwBG13i" BRIGHT_DATA_PROXY_SERVER = "http://zproxy.lum-superproxy.io:22225" BRIGHT_DATA_PROXY_USERNAME = "lum-customer-c_daabba94-zone-residential-country-fr" +BRIGHT_DATA_MOBILE_PROXY_USERNAME = "lum-customer-c_daabba94-zone-mobile-country-fr-city-paris-mobile" BRIGHT_DATA_PROXY_PASSWORD = "9dwmh54u3bbh" +BRIGHT_DATA_MOBILE_PROXY_PASSWORD = "fk5f7c2z2c19" PROXY_NAME_PREFIX_RES = "panleicim-res-fr-" PROXY_NAME_PREFIX_CC = "panleicim-cc-fr-" @@ -37,10 +39,15 @@ def get_proxy(proxy_type=0): "password": PROXY_PASSWORD } else: + # proxy = { + # "server": BRIGHT_DATA_PROXY_SERVER, + # "username": BRIGHT_DATA_PROXY_USERNAME, + # "password": BRIGHT_DATA_PROXY_PASSWORD + # } proxy = { "server": BRIGHT_DATA_PROXY_SERVER, - "username": BRIGHT_DATA_PROXY_USERNAME, - "password": BRIGHT_DATA_PROXY_PASSWORD + "username": BRIGHT_DATA_MOBILE_PROXY_USERNAME, + "password": BRIGHT_DATA_MOBILE_PROXY_PASSWORD } return proxy diff --git a/workers/commandor_page.py b/workers/commandor_page.py index 3b78e95..bd7b6aa 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -102,6 +102,7 @@ class CommandorPage: proxy = params.get_proxy(self.proxy_type) # wait for sms_code field # self.clickOnValidBtn() + time.sleep(4000) self.thread_event = e otp_input = self.page.locator(OTP_FIELD_ID) otp_input.wait_for(state='visible', timeout=TIME_OUT) @@ -374,17 +375,12 @@ def on_success(result: ReserveResultPojo): def launch_page(): - proxy = { - "server": params.BRIGHT_DATA_PROXY_SERVER, - "username": params.BRIGHT_DATA_PROXY_USERNAME, - "password": params.BRIGHT_DATA_PROXY_PASSWORD - } passport_number = get_random_id_number() contact = ContactPojo(phone_number="+33758912245", passport_number=passport_number, last_name="XU", first_name="xingzhen", mail="ColbyPatel653@gmail.com", ccid="", position=0) page = CommandorPage(contact, store_type=1) - return page.start_page(proxy) + return page.start_page(params.get_proxy(0)) def wait_for_otp(event: threading.Event, commandor: CommandorPage): From 5dcb0f6bbb1bc7c8b6b77f437da54d5f43260dd0 Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Thu, 9 Jun 2022 10:23:22 +0200 Subject: [PATCH 10/11] remove used waiting --- workers/commandor_page.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/workers/commandor_page.py b/workers/commandor_page.py index 65ed105..d6718d6 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -7,8 +7,6 @@ import time from typing import Union from playwright.sync_api import sync_playwright - -import definitions import params from pojo.ModeEnum import ModeEnum from pojo.ReserveResultPojo import ReserveResultPojo, PublishType @@ -102,7 +100,6 @@ class CommandorPage: proxy = params.get_proxy(self.proxy_type) # wait for sms_code field # self.clickOnValidBtn() - time.sleep(4000) self.thread_event = e otp_input = self.page.locator(OTP_FIELD_ID) otp_input.wait_for(state='visible', timeout=TIME_OUT) From d1699c295fc2a5d46657acb675594a2af2381213 Mon Sep 17 00:00:00 2001 From: Lei PAN Date: Mon, 13 Jun 2022 11:42:54 +0200 Subject: [PATCH 11/11] add BrightData proxy support --- appointment.py | 22 ++++++++++------------ check_results.py | 7 ++----- main.py | 9 +++++---- params.py | 28 +++++++++++++--------------- proxy/__init__.py | 0 proxy/proxy_type.py | 6 ++++++ workers/commandor_page.py | 6 ++++-- 7 files changed, 40 insertions(+), 38 deletions(-) create mode 100644 proxy/__init__.py create mode 100644 proxy/proxy_type.py diff --git a/appointment.py b/appointment.py index 9e07a1a..f573bee 100644 --- a/appointment.py +++ b/appointment.py @@ -3,14 +3,15 @@ import PySimpleGUI as sg # First the window layout in 2 columns from main import start_book from pojo.ModeEnum import ModeEnum +from proxy.proxy_type import ProxyType KEY_CHOOSE_STORE = "CHOOSE_STORE" KEY_START_NUMBER = "KEY_START_NUMBER" KEY_END_NUMBER = "KEY_END_NUMBER" KEY_MAX_WORKERS = "KEY_MAX_WORKERS" KEY_RANDOM = "KEY_RANDOM" -KEY_PROXY_RES = "KEY_PROXY_RES" -KEY_PROXY_CC = "KEY_PROXY_CC" +KEY_PROXY_BRIGHTDATA = "KEY_PROXY_BRIGHTDATA" +KEY_PROXY_NETNUT = "KEY_PROXY_NETNUT" KEY_FAUBOURG = "KEY_FAUBOURG" KEY_GEORGE = "KEY_GEORGE" KEY_SEVRES = "KEY_SEVRES" @@ -37,8 +38,8 @@ store_settings_column = [ ] proxy_settings_column = [ [sg.Text("代理ip池")], - [sg.Radio('res(速度)', group_id=GROUP_PROXY, key=KEY_PROXY_RES, default=True)], - [sg.Radio('cc(稳定)', group_id=GROUP_PROXY, key=KEY_PROXY_CC, default=False)], + [sg.Radio('亮数据', group_id=GROUP_PROXY, key=KEY_PROXY_BRIGHTDATA, default=True)], + [sg.Radio('Netnut', group_id=GROUP_PROXY, key=KEY_PROXY_NETNUT, default=False)], ] mode_settings_column = [ @@ -76,19 +77,16 @@ while True: elif values[KEY_SEVRES]: store_type = 3 - proxy_type = 0 - if values[KEY_PROXY_CC]: - proxy_type = 1 - elif values[KEY_PROXY_RES]: - proxy_type = 0 + proxy_type = ProxyType.BRIGHT_DATA + if values[KEY_PROXY_NETNUT]: + proxy_type = ProxyType.NETNUT + elif values[KEY_PROXY_BRIGHTDATA]: + proxy_type = ProxyType.BRIGHT_DATA if values[KEY_AUTOMATIC]: mode = ModeEnum.AUTOMATIC start_book(start_line, end_line, store_choose_state=store_type, max_workers=max_workers, proxy_type=proxy_type, mode=mode) - # except Exception as error: - # print("Not Integer: ") - # print(error) elif event == "Exit" or event == sg.WIN_CLOSED: break diff --git a/check_results.py b/check_results.py index b81d7cf..9a6fefc 100644 --- a/check_results.py +++ b/check_results.py @@ -12,6 +12,7 @@ from notification.AcceptedResultPojo import get_accepted_result_from from notification.mailer import Mailer from pojo.ReserveResultPojo import ReserveResultPojo from pojo.ResultEnum import ResultEnum +from proxy.proxy_type import ProxyType SORRY_SENTENCE_FR = "nous sommes sincèrement désolés de n'avoir pu vous satisfaire cette fois-ci" SORRY_SENTENCE_EN = "we are extremely sorry that we were not able to fulfill" @@ -72,11 +73,7 @@ class ResultChecker: } while content is None: content = self.load_page(self.tls.playwright, proxy, url) - proxy = { - "server": params.BRIGHT_DATA_PROXY_SERVER, - "username": params.BRIGHT_DATA_PROXY_USERNAME, - "password": params.BRIGHT_DATA_PROXY_PASSWORD - } + proxy = params.get_proxy(ProxyType.NETNUT) print(content) self.browser.close() print("Stopped worker in ", threading.current_thread().name) diff --git a/main.py b/main.py index f36a574..2886742 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ import params from logs.AppLogging import init_logger from pojo.ModeEnum import ModeEnum from pojo.contact_pojo import ContactPojo +from proxy.proxy_type import ProxyType from utils.excel_reader import ExcelHelper from workers.commandor_page import CommandorPage @@ -15,7 +16,7 @@ logger = logging.getLogger() logger.addHandler(logging.StreamHandler(stream=sys.stdout)) -def start_book(start_number, end_number, store_choose_state=0, max_workers=10, proxy_type=0, +def start_book(start_number, end_number, store_choose_state=0, max_workers=10, proxy_type=ProxyType.BRIGHT_DATA, mode: ModeEnum = ModeEnum.MANUAL): # read the contact, and contact the 2 objects together excel_reader = ExcelHelper() @@ -35,9 +36,9 @@ def start_book(start_number, end_number, store_choose_state=0, max_workers=10, p def recheck_the_captcha_error_contacts(store_type=0, mode: ModeEnum = ModeEnum.MANUAL): # get all the contacts in captcha error - list = params.local_db_manager.get_all_captcha_error_contacts() + contact_list = params.local_db_manager.get_all_captcha_error_contacts() with ThreadPoolExecutor(max_workers=10) as executor: - for errorContact in list: + for errorContact in contact_list: contact = ContactPojo.get_contact_from_error_contact(errorContact) proxy = get_proxy() # start the task in thread @@ -46,7 +47,7 @@ def recheck_the_captcha_error_contacts(store_type=0, mode: ModeEnum = ModeEnum.M proxy) -def get_proxy(proxy_type=0): +def get_proxy(proxy_type=ProxyType.BRIGHT_DATA): return params.get_proxy(proxy_type) diff --git a/params.py b/params.py index 126eb0e..573564a 100644 --- a/params.py +++ b/params.py @@ -5,6 +5,7 @@ import definitions from db.DbManager import DataManager from db.local_db_manager import LocalDbManager from logs.LogSender import LogSender +from proxy.proxy_type import ProxyType firebase_store_manager = DataManager() oracle_log_sender = LogSender() @@ -22,33 +23,30 @@ PROXY_NAME_PREFIX_RES = "panleicim-res-fr-" PROXY_NAME_PREFIX_CC = "panleicim-cc-fr-" -def get_proxy_name_prefix(proxy_type=0) -> str: - if proxy_type == 0: - return PROXY_NAME_PREFIX_RES - else: - return PROXY_NAME_PREFIX_CC +def get_proxy_name_prefix() -> str: + return PROXY_NAME_PREFIX_RES -def get_proxy(proxy_type=0): - if proxy_type == 0: +def get_proxy(proxy_type: ProxyType): + if proxy_type == ProxyType.NETNUT: random_id_number = get_random_id_number_for_proxy() - proxy_username = get_proxy_name_prefix(proxy_type) + random_id_number + proxy_username = get_proxy_name_prefix() + random_id_number proxy = { "server": PROXY_SERVER, "username": proxy_username, "password": PROXY_PASSWORD } else: - # proxy = { - # "server": BRIGHT_DATA_PROXY_SERVER, - # "username": BRIGHT_DATA_PROXY_USERNAME, - # "password": BRIGHT_DATA_PROXY_PASSWORD - # } proxy = { "server": BRIGHT_DATA_PROXY_SERVER, - "username": BRIGHT_DATA_MOBILE_PROXY_USERNAME, - "password": BRIGHT_DATA_MOBILE_PROXY_PASSWORD + "username": BRIGHT_DATA_PROXY_USERNAME, + "password": BRIGHT_DATA_PROXY_PASSWORD } + # proxy = { + # "server": BRIGHT_DATA_PROXY_SERVER, + # "username": BRIGHT_DATA_MOBILE_PROXY_USERNAME, + # "password": BRIGHT_DATA_MOBILE_PROXY_PASSWORD + # } return proxy diff --git a/proxy/__init__.py b/proxy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/proxy/proxy_type.py b/proxy/proxy_type.py new file mode 100644 index 0000000..ca78fa3 --- /dev/null +++ b/proxy/proxy_type.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class ProxyType(Enum): + NETNUT = "NETNUT" + BRIGHT_DATA = "BRIGHT_DATA" diff --git a/workers/commandor_page.py b/workers/commandor_page.py index d6718d6..19ba28a 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -13,6 +13,7 @@ from pojo.ReserveResultPojo import ReserveResultPojo, PublishType from pojo.captcha_error_contact_pojo import ERROR_TYPE_CAPTCHA, \ TOO_MANY_REQUEST_ERROR from pojo.contact_pojo import ContactPojo +from proxy.proxy_type import ProxyType from utils.generate_random_passport_id import get_captcha_error_contact_from_contact from workers.SolveCaptch import SolveCaptcha @@ -52,7 +53,8 @@ class Tls(threading.local): class CommandorPage: tls = Tls() - def __init__(self, contact: ContactPojo, store_type=0, proxy_type=0, mode: ModeEnum = ModeEnum.MANUAL): + def __init__(self, contact: ContactPojo, store_type=0, proxy_type=ProxyType.BRIGHT_DATA, + mode: ModeEnum = ModeEnum.MANUAL): self.otp_value = None self.logger = logging.getLogger("约会页面:" + str(contact.phone)) self.is_finished = False @@ -379,7 +381,7 @@ def launch_page(): first_name="xingzhen", mail="ColbyPatel653@gmail.com", ccid="", position=0) page = CommandorPage(contact, store_type=1) - return page.start_page(params.get_proxy(0)) + return page.start_page(params.get_proxy()) def wait_for_otp(event: threading.Event, commandor: CommandorPage):