diff --git a/.gitignore b/.gitignore index ed351c5..606c743 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ lib appointment_*.log ./build ./dist +./contact_all.xlsx diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..bb823c2 --- /dev/null +++ b/Info.plist @@ -0,0 +1,14 @@ + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.security.codesign + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + codesign + CFBundleVersion + 0.3 + + \ No newline at end of file diff --git a/appointment.py b/appointment.py index 082a2a3..c505dbf 100644 --- a/appointment.py +++ b/appointment.py @@ -8,10 +8,13 @@ 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_FAUBOURG = "KEY_FAUBOURG" KEY_GEORGE = "KEY_GEORGE" KEY_SEVRES = "KEY_SEVRES" GROUP_STORE = "STORE" +GROUP_PROXY = "GROUP_PROXY" file_list_column = [ [sg.Text('请输入联系人表的起始和结束行')], @@ -21,18 +24,24 @@ file_list_column = [ [sg.Submit(button_text="运行", key="submit")] ] # For now will only show the name of the file that was chosen -settings_column = [ +store_settings_column = [ [sg.Text("目标商店")], [sg.Radio('随机', group_id=GROUP_STORE, key=KEY_RANDOM, default=True)], [sg.Radio('Hermès Faubourg Saint-Honoré', group_id=GROUP_STORE, key=KEY_FAUBOURG, default=False)], [sg.Radio('Hermès George V', group_id=GROUP_STORE, key=KEY_GEORGE, default=False)], [sg.Radio('Hermès Sèvres', group_id=GROUP_STORE, key=KEY_SEVRES, default=False)] ] +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)], +] # ----- Full layout ----- layout = [ [ sg.Column(file_list_column), - sg.Column(settings_column) + sg.Column(store_settings_column), + sg.Column(proxy_settings_column) ] ] @@ -54,7 +63,13 @@ while True: store_type = 2 elif values[KEY_SEVRES]: store_type = 3 - start_book(start_line, end_line, store_choose_state=store_type, max_workers=max_workers) + + proxy_type = 0 + if values[KEY_PROXY_CC]: + proxy_type = 1 + elif values[KEY_PROXY_RES]: + proxy_type = 0 + start_book(start_line, end_line, store_choose_state=store_type, max_workers=max_workers, proxy_type=proxy_type) # except Exception as error: # print("Not Integer: ") # print(error) diff --git a/appointment.spec b/appointment.spec index 0ef900d..7ce8e5c 100644 --- a/appointment.spec +++ b/appointment.spec @@ -8,7 +8,7 @@ a = Analysis( ['appointment.py'], pathex=[], binaries=[], - datas=[], + datas=[('appointment.json','.')], hiddenimports=[], hookspath=[], hooksconfig={}, @@ -49,7 +49,6 @@ coll = COLLECT( name='appointment', ) import shutil -shutil.copyfile('./appointment.json', '{0}/appointment.json'.format(DISTPATH)) shutil.copyfile('./contact_all.xlsx', '{0}/contact_all.xlsx'.format(DISTPATH)) shutil.copyfile('./config.ini', '{0}/config.ini'.format(DISTPATH)) -shutil.copytree('./venv/site-packages/grpc/_cython/_credentials', '{0}/appointment/grpc/_cython/_credentials'.format(DISTPATH)) \ No newline at end of file +#shutil.copytree('./venv/Lib/site-packages/grpc/_cython/_credentials', '{0}/appointment/grpc/_cython/_credentials'.format(DISTPATH)) \ No newline at end of file diff --git a/appointment_mac.spec b/appointment_mac.spec index ddb9e2c..e02fd55 100644 --- a/appointment_mac.spec +++ b/appointment_mac.spec @@ -8,7 +8,7 @@ a = Analysis( ['appointment.py'], pathex=[], binaries=[], - datas=[], + datas=[('appointment.json','.')], hiddenimports=[], hookspath=[], hooksconfig={}, @@ -49,7 +49,6 @@ coll = COLLECT( name='appointment', ) import shutil -shutil.copyfile('./appointment.json', '{0}/appointment.json'.format(DISTPATH)) shutil.copyfile('./contact_all.xlsx', '{0}/contact_all.xlsx'.format(DISTPATH)) shutil.copyfile('./config.ini', '{0}/config.ini'.format(DISTPATH)) shutil.copytree('./venv/lib/python3.8/site-packages/grpc/_cython/_credentials', '{0}/appointment/grpc/_cython/_credentials'.format(DISTPATH)) \ No newline at end of file diff --git a/contact_all.xlsx b/contact_all.xlsx deleted file mode 100644 index 2d502af..0000000 Binary files a/contact_all.xlsx and /dev/null differ diff --git a/definitions.py b/definitions.py index a73a5e7..006fdbb 100644 --- a/definitions.py +++ b/definitions.py @@ -1,8 +1,14 @@ import configparser import os import getpass +from pathlib import Path + +home = str(Path.home()) config = configparser.ConfigParser() -config.read('./config.ini') +print("home path: " + home) +# check the config file exsistence +config_file_path = home + "/config.ini" +config.read(config_file_path) CONTACT_LIST_FILE = config['DEFAULT']['contact_list_file'] FIREBASE_CONFIG_FILE = config['DEFAULT']['firebase_config_file'] LOGS_DIR = config['DEFAULT']['LOGS_DIR'] diff --git a/main.py b/main.py index 45a2d81..28397d6 100644 --- a/main.py +++ b/main.py @@ -13,7 +13,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): +def start_book(start_number, end_number, store_choose_state=0, max_workers=10, proxy_type=0): # read the contact, and contact the 2 objects together excel_reader = ExcelHelper() all_contacts = excel_reader.read_contacts() @@ -23,14 +23,16 @@ def start_book(start_number, end_number, store_choose_state=0, max_workers=10): logger.info(contacts) with ThreadPoolExecutor(max_workers=max_workers) as executor: for contact in contacts: - proxy = get_proxy(contact.phone) + proxy = get_proxy(contact.phone, proxy_type) # start the task in thread - executor.submit(CommandorPage(contact, store_type=store_choose_state).start_page, proxy) + executor.submit(CommandorPage(contact, store_type=store_choose_state, proxy_type=proxy_type).start_page, + proxy) -def get_proxy(phone_number): - random_id_number = str(phone_number)[1:len(str(phone_number))] - proxy_username = "panleicim-res-fr-" + random_id_number +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.PROXY_SERVER, @@ -42,4 +44,4 @@ def get_proxy(phone_number): if __name__ == '__main__': # 修改联系人行,结束联系人行 第三个参数store等于0的时候是随机,传入1的时候是总店 - start_book(16, 16, store_choose_state=0) + start_book(16, 16, store_choose_state=0, proxy_type=0) diff --git a/params.py b/params.py index a5db2db..c3f32f7 100644 --- a/params.py +++ b/params.py @@ -10,6 +10,14 @@ oracle_log_sender = LogSender() # proxy PROXY_SERVER = "http://gw.ntnt.io:5959" PROXY_PASSWORD = "94sY7zwBG13i" +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_random_id_number_for_proxy() -> str: diff --git a/pojo/ReserveResultPojo.py b/pojo/ReserveResultPojo.py index 63586c8..5c6b23f 100644 --- a/pojo/ReserveResultPojo.py +++ b/pojo/ReserveResultPojo.py @@ -10,6 +10,7 @@ import definitions class PublishType(Enum): SUCCESS = "SUCCESS" ERROR = "ERROR" + PENDING = "PENDING" @dataclass_json diff --git a/utils/excel_reader.py b/utils/excel_reader.py index ca72027..df90efc 100644 --- a/utils/excel_reader.py +++ b/utils/excel_reader.py @@ -24,7 +24,11 @@ class ExcelHelper: raw_name = contact_dict['name'].strip() name = raw_name.split(' ') last_name = name[0] - first_name = name[-1] + if len(name) == 2: + first_name = name[-1] + else: + first_name = ''.join(name[1:len(name)]) + contact = ContactPojo(phone_number=contact_dict['phone'], last_name=last_name, first_name=first_name, diff --git a/workers/commandor_page.py b/workers/commandor_page.py index 107c88a..d7f74a2 100644 --- a/workers/commandor_page.py +++ b/workers/commandor_page.py @@ -1,5 +1,6 @@ import logging import random +import re import string import threading import time @@ -18,6 +19,7 @@ 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://bot.sannysoft.com/" +REGEX_RDV_URL = "https:\/\/rendezvousparis\.hermes\.com\/client\/register\/[A-Z0-9]+" otp_value = None OTP_FIELD_ID = "#sms_code" MESSAGE_FIELD_CLASS = ".message" @@ -28,10 +30,11 @@ TOO_MANY_REQUEST_ERROR_MESSAGE = "Due to a large number of requests" CAPTCHA_ERROR_MESSAGE = "Error verifying captcha, please try again" TIME_OUT = 400000 OTP_TIMEOUT = 240 +PAGE_TIMEOUT = 30000 def get_random_wait_time() -> float: - wait_time = random.randint(0, 10) / 10.0 * 5 + wait_time = random.randint(0, 10) / 10.0 * 1 return wait_time @@ -43,11 +46,12 @@ class Tls(threading.local): class CommandorPage: tls = Tls() - def __init__(self, contact: ContactPojo, store_type=0): + def __init__(self, contact: ContactPojo, store_type=0, proxy_type=0): self.otp_value = None self.logger = logging.getLogger("CommandorPage") self.is_finished = False self.contact = contact + self.proxy_type = proxy_type # 0: random # 1: faubourg # 2: George @@ -72,7 +76,7 @@ class CommandorPage: self.termine() def _run(self, e: threading.Event, proxy): - print("will start browser") + self.logger.info("will start browser") self.on_success_listener = on_success # reset otp_value to None self.otp_value = None @@ -80,21 +84,14 @@ class CommandorPage: first_page = None while first_page is None: first_page = self.start_browser(proxy, self.tls.playwright, devices) - proxy_username = "panleicim-res-fr-" + params.get_random_id_number_for_proxy() + 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 } - self._setName(self.contact.last_name, self.contact.first_name) - self._setPhoneCountryAndStore() - self._setPhoneNumber(self.contact.phone) - self._set_email(self.contact.mail) - self.setIdNumber(self.contact.passport) - # - self._checkCgu() - self.click_recapcha_checkbox() + self.fill_fields() # wait for sms_code field # self.clickOnValidBtn() self.thread_event = e @@ -119,9 +116,18 @@ class CommandorPage: self.logger.info("timeout") self.termine() + def fill_fields(self): + self._set_name(self.contact.last_name, self.contact.first_name) + self._setPhoneCountryAndStore() + self._setPhoneNumber(self.contact.phone) + self._set_email(self.contact.mail) + self.setIdNumber(self.contact.passport) + # + self._checkCgu() + def start_browser(self, proxy, pwright, device) -> Union[str, None]: try: - self.browser = pwright.webkit.launch(headless=False, timeout=90000, proxy=proxy) + self.browser = pwright.webkit.launch(headless=False, timeout=PAGE_TIMEOUT, proxy=proxy) self.logger.info("模拟设备: " + device) pixel_2 = pwright.devices[device] context = self.browser.new_context(**pixel_2, locale='en-GB') @@ -136,7 +142,8 @@ class CommandorPage: } """) self.page.on("load", self._on_page_loaded) - self.page.goto(RDV_URL, timeout=90000) + self.page.on("response", self.handle_response) + self.page.goto(RDV_URL, timeout=PAGE_TIMEOUT) return self.page.content() except Exception as error: params.oracle_log_sender.send_error(str(error)) @@ -145,6 +152,12 @@ class CommandorPage: self.browser.close() return None + def handle_response(self, response): + pattern = re.compile(REGEX_RDV_URL) + if pattern.match(response.url): + self.logger.info("result url found: " + response.url) + self.publish_message_to_queue(self.contact, PublishType.PENDING, response.url) + def start_page(self, proxy): e = threading.Event() self._run(e, proxy) @@ -153,6 +166,8 @@ class CommandorPage: self.logger.info("page loaded") # self.logger.info("content is " + self.page.content()) self.logger.info("url is " + self.page.url) + if self.page.url == RDV_URL: + self.fill_fields() message = self.page.content() if CONFIRMED_MESSAGE in message: # publish the successful message @@ -185,14 +200,18 @@ class CommandorPage: except Exception as error: self.logger.error(error) - def _setName(self, lastName, firstName): + def _set_name(self, lastName, firstName): time.sleep(get_random_wait_time()) try: - self.page.evaluate("""(name)=> { - document.getElementById("surname").focus(); - document.getElementById("surname").value = name.lastName; + self.page.evaluate("""(name)=> { + let surname = document.getElementById("surname"); + if(surname.value.length == 0){ + surname.focus(); + surname.value = name.lastName; document.getElementById("name").focus(); - document.getElementById("name").value = name.firstName}""", {'lastName': lastName, 'firstName': firstName}) + document.getElementById("name").value = name.firstName + }} + """, {'lastName': lastName, 'firstName': firstName}) except Exception as error: self.logger.error(error) @@ -229,8 +248,10 @@ class CommandorPage: time.sleep(get_random_wait_time()) try: self.page.evaluate("""(email)=>{ - document.getElementById("email").focus(); - document.getElementById("email").value = email;}""", email) + let emailElement = document.getElementById("email") + if(emailElement.value.length == 0){ + emailElement.focus(); + document.getElementById("email").value = email;}}""", email) except Exception as error: self.logger.error(error)