def store_check(areaId): """ This is different with ireserve_ctrl.storeCheck. No matter which store and whatever the model is, this function will return true 只是为了提供登录用的URL,以方便获取短信验证码。根据appleId所对应的城市, 任何一家直营店的任何有库存的型号即可生成登录URL。 :returns: TODO """ availurl = configHelper.readURL("availURL") avail_json = requests.get(availurl).json() if len(avail_json) < 1: # Apple.com reservation is not available yet print(bcolors.FAIL + time.strftime('%d, %b %Y %H:%M:%S') + " - Data Unavailable.") return else: stores = dao.getStoresByArea(areaId) for item in stores: storeId = item[0] allModels = avail_json.get(storeId) for modelId in allModels: if allModels[modelId] == "ALL": # 如果有货 reserve_info = { "modelId": modelId, "storeId": storeId, "areaId": areaId, } return reserve_info
def storeCheck(self): # imp.reload(sys) availurl = configHelper.readURL("availURL") # response = urllib2.urlopen(availurl) # content = response.read() # avail_json = json.loads(content) headers = myutil.getHTMLHeaders() avail_json = requests.get(availurl, headers=headers).json() # Apple.com reservation is not available yet if len(avail_json) < 1: print(bcolors.FAIL + time.strftime('%d, %b %Y %H:%M:%S') + " - Data Unavailable.") return # 组织好area-model-clients数据结构 print("--------------- area-model-clients 数据结构 -----------------") print(bcolors.OKBLUE + myutil.format_dict(self.__maindict)) # 遍历area-model-clients数据结构 for areaId, areaDict in self.__maindict.items(): stores = self.__dao.getStoresByArea(areaId) # 对于每一个地区的所有店铺遍历 for item in stores: storeId = item[0] storeName = item[1] areaName = item[2] storeStr = storeName + ", " + areaName + ", " + storeId print(bcolors.OKGREEN + storeStr) # 过滤出当前店铺所有产品 allModels = avail_json.get(storeId) for modelId in allModels: # 如果当前产品id在顾客选择的产品范围之内,并且有库存的话 if modelId in areaDict and allModels[modelId] == "ALL": reserve_info = { "modelId": modelId, "storeId": storeId, "clientInfo": areaDict[modelId][0], "areaId": areaId, "storeStr": storeStr, } clientInfo = areaDict[modelId][0] applog_info = { "storeName": storeName, "areaName": areaName, "modelName": self.__models[modelId], "fengId": clientInfo["fengId"], "appleId": clientInfo["appleId"], } return reserve_info, applog_info print(bcolors.FAIL + "Nothing Available\n") print(bcolors.OKBLUE + "Updated: " + time.strftime('%d, %b %Y %H:%M:%S') + "\n")
def do_reserve(self, areaId, storeId, modelId, clientInfo, storeStr): # Get initial request URL modelSelectURL = configHelper.readURL("modelSelectURL") url = modelSelectURL + "&partNumber=" + modelId + "&store=" + storeId if not self.__test: print(bcolors.OKGREEN + modelId + " - " + self.__models[modelId]) self.__logger.info(modelId + " - " + self.__models[modelId] + " - " + storeStr) os.system('espeak "Congratulations!"') print(u"当前的预约信息: " + areaId + " " + storeId + " " + modelId) print(u"客户信息 -- " + myutil.format_dict(clientInfo, tab=1)) IReserve = myutil.get_class(configHelper.readEngine()) ir = IReserve() errorList = ir.reserve(url, clientInfo, self.__test) return errorList
def sms_update(clientInfo, lock): """ Support reservation in different city with same appleId. As long as within half an hour, the same rcode can be used.Though different processed would be created based on records with same appleId in table 'rcode' still the same 支持同一账号不同地区的预约。只要是同一账号验证码半小时内是通用的。 虽然会根据rcode表中的相同的appleId启动不同的进程,但效果是一样的。 """ print("inside sms_update") while True: # 判断当前客户是否是active的, 如果已经是无效客户则立即退出子进程 isActive = dao.getClientInfo(clientInfo["oid"])[9] if not isActive: return # 获得上一次更新rcode的时间, 计算出时间差 phoneNumber = configHelper.readPhoneNumber(False) rcodeResult = dao.getRCode(phoneNumber, clientInfo["appleId"]) if rcodeResult: last_time = datetime.datetime.strptime( rcodeResult[0][4], "%Y-%m-%d %H:%M:%S") time_diff = round((datetime.datetime.now() - last_time) .total_seconds() / 60, 2) else: time_diff = 999 # 如果时间差超过30分钟,则需要更新rcode if time_diff >= 30: # 获得有效库存信息,用来生成登录URL reserve_info = store_check(clientInfo["area"]) if reserve_info is None: print(u"当前区域 %s 已关闭预约" % clientInfo["area"]) return storeId = reserve_info["storeId"] modelId = reserve_info["modelId"] # Get initial request URL modelSelectURL = configHelper.readURL("modelSelectURL") url = modelSelectURL + "&partNumber=" + modelId +\ "&store=" + storeId print (u"客户信息 -- " + myutil.format_dict(clientInfo, tab=1)) # 获得当天已发短信数目和当天已成功预约数 cellPhoneInfo = dao.getCellphoneInfo(phoneNumber) todayCount = cellPhoneInfo[2] + 1 availCount = cellPhoneInfo[3] # There might be maximum limit for messages per day # if todayCount > 100: # os.system('espeak "already five messages"') # return ir = IReserveHTTP() rCode = None try: rCode = ir.get_avail_rcode(url, clientInfo, lock) except errors.IReserveLoginError as e: os.system('espeak "login or reserve error"') traceback.print_exc() ctrl = ireserve_ctrl.IReserveCtrl() ctrl.inactiveClient(clientInfo["oid"]) except errors.IReserveLoginFastError as e: os.system('espeak "login too fast"') traceback.print_exc() print("sleeping 10 minutes") time.sleep(10 * 60) except errors.IReserveSMSError as e: os.system('espeak "phone number error"') raise e except errors.ISMSTimeoutError as e: os.system('espeak "SMS timeout"') traceback.print_exc() time.sleep(configHelper.readConfig("loginsleep")) if rCode: curTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") dao.insertOrUpdateRCode( rCode, phoneNumber, clientInfo["appleId"], curTime) # 更新对应手机号的信息,其有效预约次数要减一 dao.updateCellphoneInfo(phoneNumber, todayCount, availCount) else: sleep_min = 30.00 - time_diff print(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) process_name = multiprocessing.current_process().name print(" Process[%s] sms_update() sleeping %s minutes..." % (process_name, sleep_min)) time.sleep(sleep_min * 60)
class IReserveHTTP(ireserve.IReserve): """HTTP request reserving class""" signinWidgetURL = configHelper.readURL("signinWidgetURL") signinURL = configHelper.readURL("signinURL") mainFormURL = configHelper.readURL("mainFormURL") def __init__(self): super(IReserveHTTP, self).__init__() print("IReserveHTTP init") self.__session = requests.Session() self.__dao = dao.Dao(False) print("IReserveHTTP init") def __del__(self): self.__session.close() print("IReserveHTTP deleted") def reserve(self, url, clientDict, test): print("IReserveHTTP reserve") self.__test = test url = self.__stepModelSel(url) url = self.__stepSignin(url, clientDict) url = self.__stepRCode(url, clientDict["appleId"]) errorList = self.__stepTimeSlot(url, clientDict["govid"], clientDict["govidType"], clientDict["quantity"]) return errorList def get_avail_rcode(self, url, clientDict, lock): print("IReserveHTTP SMS udpate") url = self.__stepModelSel(url) url = self.__stepSignin(url, clientDict) with lock: rcode = self.__stepRCodeUpdate(url, clientDict["appleId"]) return rcode def __getJSONHeaders(self): headers = { "Accept": "application/json, text/javascript, */*; q=0.01", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.6,en;q=0.4", "Connection": "keep-alive", "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest", } return headers # ==================== Step 1 : select your model ======================== def __stepModelSel(self, url): print("================= Step 1 : select your model =================") print("-------- Step 1.1 select your model and submit ---------"), '\n' # Modify request headers headers = myutil.getHTMLHeaders() self.__session.headers = headers r = self.__session.get(url, allow_redirects=True) print(r.url), '\n' return r.url # ================== Step 2 : login submit ================================ def __stepSignin(self, url, clientDict): print("================ Step 2 : login submit =======================") redirectedLoginURL = url print(" ----------- Step 2.2 display iframe login widget -----------") r = self.__session.get(IReserveHTTP.signinWidgetURL, allow_redirects=True) print(" -------------- Step 2.3 OMG!!! LOGIN!!!!! --------------") # Refresh the headers self.__session.headers = self.__getJSONHeaders() self.__session.headers.update({ "X-Apple-App-Id": "942", "X-Apple-Locale": "CN-ZH", "X-Apple-Widget-Key": "40692a3a849499c31657eac1ec8123aa", }) payload = { "accountName": clientDict["appleId"], "password": clientDict["pwd"], "rememberMe": False } r = self.__session.post(IReserveHTTP.signinURL, json=payload) print(" ------LOGIN JSON RESULT-----" + r.text) error_str = self.__json_errmsg(r.text, "serviceErrors") print(error_str) if len(error_str) > 0: raise errors.IReserveLoginError(error_str) print(" -------------- Step 2.4 Submit main login form -------------") self.__session.headers = myutil.getHTMLHeaders() rs = urlparse.urlparse(redirectedLoginURL) q = urlparse.parse_qs(rs.query) data = { "rememberMe": False, "appIdKey": q["appIdKey"], "language": q["language"], "path": q["path"], "oAuthToken": "", "rv": q["rv"] } r = self.__session.post(IReserveHTTP.mainFormURL, data=data) print('main login form redirect ' + r.url, '\n') return r.url # ================== Step 3 : Registration Code ======================== def __stepRCode(self, url, appleId): print("================= Step 3 : Registration Code ================") print('---------- step 3.1 Request SMS JSON ------------------') rCodeURL = url + "&ajaxSource=true&_eventId=context" print(rCodeURL, '\n') self.__session.headers = self.__getJSONHeaders() print("request URL: " + rCodeURL), '\n' r = self.__session.get(rCodeURL) print('--------[SMS JSON RESULT]---' + r.text + '---------------') if not r.text: raise errors.IReserveLoginFastError("登录太快了,请等几秒钟") print('---------- step 3.2 Send SMS & get RCode ------------'), '\n' rCode = "" # If smscode is already sended print(json.loads(r.text)), '\n' rcDict = json.loads(r.text) smsCode = rcDict['keyword'] print('SMS Code: ' + rcDict['keyword']) print(self.__test) p_ie = rcDict["p_ie"] flowExecutionKey = rcDict['_flowExecutionKey'] firstTime = rcDict['firstTime'] autosms = configHelper.readMode('autosms') if not self.__test or autosms: phoneNumber = configHelper.readPhoneNumber(False) else: phoneNumber = configHelper.readPhoneNumber(True) print("phone number: " + phoneNumber) if firstTime: if not self.__test or autosms: CMPhoneNumber = configHelper.readCMPhoneNumber(self.__test) # rCode = "new code" rCode = smsmode.getResrictionCode(CMPhoneNumber, smsCode) else: # Manually input rcode rCode = raw_input("please input registration code:") # configHelper.writeRCode(rCode, appleId) curTime = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") self.__dao.insertOrUpdateRCode(rCode, phoneNumber, appleId, curTime) else: # Last rCode is still available # rCode = configHelper.readRCode() rCode = self.__dao.getRCode(phoneNumber, appleId)[0][1] print('registration code: ' + rCode) self.__session.headers = myutil.getHTMLHeaders() data = { "phoneNumber": phoneNumber, "selectedCountryCode": 86, "registrationCode": rCode, "submit": "", "_flowExecutionKey": flowExecutionKey, "_eventId": "next", "p_ie": p_ie, "dims": "" } print("request URL: " + url), '\n' r = self.__session.post(url, data=data) print(r.url), '\n' return r.url def __stepRCodeUpdate(self, url, appleId): print("================= Step 3 : Registration Code ================") print('---------- step 3.1 Request SMS JSON ------------------') rCodeURL = url + "&ajaxSource=true&_eventId=context" print(rCodeURL, '\n') self.__session.headers = self.__getJSONHeaders() print("request URL: " + rCodeURL), '\n' r = self.__session.get(rCodeURL) print('--------[SMS JSON RESULT]---' + r.text + '---------------') if not r.text: raise errors.IReserveLoginFastError("登录太快了,请等几秒钟") print('---------- step 3.2 Send SMS & get RCode ------------'), '\n' rCode = "" # If smscode is already sended print(json.loads(r.text)), '\n' rcDict = json.loads(r.text) smsCode = rcDict['keyword'] print('SMS Code: ' + rcDict['keyword']) firstTime = rcDict['firstTime'] if firstTime: CMPhoneNumber = configHelper.readCMPhoneNumber(False) rCode = smsmode.getResrictionCode(CMPhoneNumber, smsCode) # configHelper.writeRCode(rCode, appleId) print('registration code: ' + rCode) return rCode # --------------- Step 4 : Select timeslot ------------------------ def __stepTimeSlot(self, url, govid, govidType, quantity): print("------------- Step 4.1 Select TimeSlots ---------------"), '\n' timeSlotURL = url + "&ajaxSource=true&_eventId=context&dims=" print("request URL: " + timeSlotURL) self.__session.headers = self.__getJSONHeaders() r = self.__session.get(timeSlotURL) print('--------[timeslots JSON RESULT]---' + r.text + '-------', '\n') error_str = self.__json_errmsg(r.text, "errors") if len(error_str) > 0: raise errors.IReserveSMSError(error_str) reserveDict = json.loads(r.text) # Finde the right time # get current hour timeslots = reserveDict["timeslots"] curHour = datetime.datetime.now().hour selectedId = "" selectedTime = "" # for timeslot in timeslots: for i, option in enumerate(timeslots): text = option['formattedTime'] curId = option['timeSlotId'] # Format : 下午 1:30 - 下午 2:00 arr = text.split(" ") hour = int(arr[1].split(":")[0]) # format hour to 24 hours if arr[0] == u"下午" and hour < 12: hour += 12 nightMode = configHelper.readMode("nightMode") if nightMode and curHour < 18: if hour >= 20: selectedId = curId selectedTime = text break else: if hour >= curHour + 2: selectedId = curId selectedTime = text break if i == len(timeslots) - 1: selectedId = curId selectedTime = text print(selectedId) print(selectedTime), '\n' # print (timeslots) print("-------------- Step 4.2 Submit Reserve ---------------"), '\n' data = { "selectedStoreNumber": reserveDict['selectedStoreNumber'], # "selectedPartNumber": partNumber, "selectedContractType": "UNLOCKED", "selectedQuantity": quantity, "selectedTimeSlotId": selectedId, "lastName": reserveDict["lastName"], "firstName": reserveDict["firstName"], "email": reserveDict["email"], "selectedGovtIdType": govidType, "govtId": govid, "p_ie": reserveDict["p_ie"], "_flowExecutionKey": reserveDict["_flowExecutionKey"], "_eventId": "next", "submit": "", } self.__session.headers = myutil.getHTMLHeaders() print(data), '\n' print("request URL: " + url), '\n' sleeptime = configHelper.readConfig('submitTimeSleep') print("current sleep time is : " + str(sleeptime)) time.sleep(sleeptime) r = self.__session.post(url, data=data) r = self.__session.get(r.url) url = r.url + "&ajaxSource=true&_eventId=context&dims=" print("request URL: " + url), '\n' self.__session.headers = self.__getJSONHeaders() r = self.__session.get(url) print('--------[Sumit Error JSON RESULT]---' + r.text + '-----', '\n') error_str = self.__json_errmsg(r.text, "errors") if len(error_str) > 0: if error_str == "availabilityError": raise errors.IReserveAvailError(error_str) else: raise errors.IReserveReserveError(error_str) # --------------- Step 5 : Submit reservation ------------------------ def __json_errmsg(self, jsonstr, errfield): print(jsonstr) jsonobj = json.loads(jsonstr) error_str = "" # If login failed if jsonobj.get(errfield): for error in jsonobj.get(errfield): if errfield == "serviceErrors": error_str += error["message"] else: error_str += error return error_str # TODO def __pp_json(json_thing, sort=True, indents=4): if type(json_thing) is str: print( json.dumps(json.loads(json_thing), sort_keys=sort, indent=indents)) else: print(json.dumps(json_thing, sort_keys=sort, indent=indents)) return None def __getCookieStr(self, cookies): cookieStr = '' if cookies is not None: for k, v in cookies.items(): cookieStr += k + '=' + v + ';' length = len(cookieStr) if length > 0: cookieStr = cookieStr[0:(length - 1)] return cookieStr