def __redeem_form(self, data): """Redeem a code with given form data""" # cache old reward list self.old_rewards = self.__query_rewards() the_url = "{}/code_redemptions".format(base_url) headers = {"Referer": "{}/new".format(the_url)} r = self.client.post(the_url, data=data, headers=headers, allow_redirects=False) _L.debug("{} {} {}".format(r.request.method, r.url, r.status_code)) status, redirect = self.__check_redemption_status(r) # did we visit /code_redemptions/...... route? redemption = False # keep following redirects while status == Status.REDIRECT: if "code_redemptions/" in redirect: redemption = True _L.debug("redirect to '{}'".format(redirect)) r2 = self.client.get(redirect) status, redirect = self.__check_redemption_status(r2) # workaround for new SHiFT website. # it doesn't tell you to launch a "SHiFT-enabled title" anymore if status == Status.NONE: if redemption: status = Status.REDEEMED redirect = "Already Redeemed" else: status = Status.TRYLATER redirect = "To continue to redeem SHiFT codes, please launch a SHiFT-enabled title first!" # noqa return status, redirect
def __get_redemption_form(self, code, platform): """Get Form data for code redemption""" the_url = "{}/code_redemptions/new".format(base_url) status_code, token = self.__get_token(the_url) if not token: _L.debug("no token") return False, status_code, "Could not retrieve Token" r = self.client.get("{base_url}/entitlement_offer_codes?code={code}" .format(base_url=base_url, **locals()), headers=json_headers(token)) _L.debug("{} {} {}".format(r.request.method, r.url, r.status_code)) soup = BSoup(r.text, "html.parser") if not soup.find("form", class_="new_archway_code_redemption"): return False, r.status_code, r.text.strip() inp = soup.find_all("input", attrs=dict(name="authenticity_token")) form_code = soup.find_all(id="archway_code_redemption_code") check = soup.find_all(id="archway_code_redemption_check") service = soup.find_all(id="archway_code_redemption_service") ind = None for i, s in enumerate(service): if platform in s["value"]: ind = i break if (ind is None): return False, r.status_code, "This code is not available for your platform" form_data = {"authenticity_token": inp[ind]["value"], "archway_code_redemption[code]": form_code[ind]["value"], "archway_code_redemption[check]": check[ind]["value"], "archway_code_redemption[service]": service[ind]["value"]} return True, r.status_code, form_data
def __check_redemption_status(self, r): """Check redemption""" import json from time import sleep if (r.status_code == 302): return Status.REDIRECT, r.headers["location"] get_status, url, fallback = self.__get_redemption_status(r) if get_status: status_code, token = self.__get_token(r) cnt = 0 while True: if cnt > 5: return Status.REDIRECT, fallback _L.info(get_status) # wait 500 _L.debug("get " + "{}/{}".format(base_url, url)) raw_json = self.client.get("{}/{}".format(base_url, url), allow_redirects=False, headers=json_headers(token)) data = json.loads(raw_json.text) if "text" in data: get_status = self.__get_status(data["text"]) return get_status sleep(0.5) cnt += 1 return Status.NONE, None
def redeem(key): """Redeem key and set as redeemed if successfull""" messages = { Status.SUCCESS: "Redeemed {key.description}", Status.EXPIRED: "This code expired by now.. ({key.description})", Status.REDEEMED: "Already redeemed {key.description}", Status.INVALID: "The code `{key.key}` is invalid", Status.TRYLATER: "Please launch a SHiFT-enabled title or wait 1 hour.", Status.UNKNOWN: "A unknown Error occured", Status.NONE: "Something unexpected happened.." } _L.debug("Trying to redeem {} ({})".format(key.description, key.key)) status = client.redeem(key.key) _L.debug("Status: {}".format(Status(status))) # set redeemed status if status in (Status.SUCCESS, Status.REDEEMED, Status.EXPIRED, Status.INVALID): query.set_redeemed(key) # notify user _L.info(messages[status].format(**locals())) return status == Status.SUCCESS
def __get_redemption_form(self, code): """Get Form data for code redemption""" the_url = "{}/code_redemptions/new".format(base_url) status_code, token = self.__get_token(the_url) if not token: _L.debug("no token") return False, status_code, "Could not retrieve Token" r = self.client.get("{base_url}/entitlement_offer_codes?code={code}" .format(base_url=base_url, **locals()), headers=json_headers(token)) soup = BSoup(r.text, "html.parser") if not soup.find("form", class_="new_archway_code_redemption"): return False, r.status_code, r.text.strip() inp = soup.find("input", attrs=dict(name="authenticity_token")) form_code = soup.find(id="archway_code_redemption_code")["value"] check = soup.find(id="archway_code_redemption_check")["value"] service = soup.find(id="archway_code_redemption_service")["value"] form_data = {"authenticity_token": inp["value"], "archway_code_redemption[code]": form_code, "archway_code_redemption[check]": check, "archway_code_redemption[service]": service} return True, r.status_code, form_data
def redeem(self, code, platform): found, status_code, form_data = self.__get_redemption_form( code, platform) # the expired message comes from even wanting to redeem if not found and form_data == "This SHiFT code has already been redeemed": status = Status.REDEEMED result = "Already Redeemed" elif not found: # entered key was invalid if status_code == 500: return Status.INVALID # entered key expired by now if "expired" in form_data: return Status.EXPIRED if "not available" in form_data: return Status.INVALID # unknown _L.error(form_data) return Status.UNKNOWN else: # the key is valid and all. status, result = self.__redeem_form(form_data) self.last_status = status _L.debug("{}: {}".format(Status(status), result)) return status
def __get_token(self, url_or_reply): """Get CSRF-Token from given URL""" if type(url_or_reply) == str: r = self.client.get(url_or_reply) _L.debug("{} {} {}".format(r.request.method, r.url, r.status_code)) else: r = url_or_reply soup = BSoup(r.text, "html.parser") meta = soup.find("meta", attrs=dict(name="csrf-token")) if not meta: return r.status_code, None return r.status_code, meta["content"]
def parse_bl2blps(game, platform): """Get all Keys from orcz""" key_urls = { "bl": "http://orcz.com/Borderlands:_Golden_Key", "bl2": "http://orcz.com/Borderlands_2:_Golden_Key", "blps": "http://orcz.com/Borderlands_Pre-Sequel:_Shift_Codes", "bl3": "http://orcz.com/Borderlands_3:_Golden_Key", "ttw": "http://orcz.com/Tiny_Tina%27s_Wonderlands:_Shift_Codes" } def check(key): """Check if key is expired (return None if so)""" ret = None span = key.find("span") if (not span) or (not span.get("style")) or ("black" in span["style"]): ret = key.text.strip() # must be of format ABCDE-FGHIJ-KLMNO-PQRST if ret.count("-") != 4: return None spl = ret.split("-") spl = [len(el) == 5 for el in spl] if not all(spl): return None return ret r = requests.get(key_urls[game]) soup = BSoup(r.text, "lxml") table = soup.find("table") rows = table.find_all("tr")[1:] for row in rows: cols = row.find_all("td") desc = cols[1].text.strip() codes = [None, None, None] for i in range(4, 7): try: codes[i - 4] = check(cols[i]) except Exception as e: # noqa _L.debug(e) pass codes.insert(1, None) for i in range(len(codes)): if codes[i]: the_platform = platforms[i] if platforms[i] in ["steam", "epic"]: the_platform = "pc" yield desc, codes[i], the_platform, game
def insert(desc, code, platform, game): """Insert Key""" el = c.execute("""SELECT * FROM keys WHERE platform = ? AND key = ? AND game = ?""", (platform, code, game)) if el.fetchone(): return _L.debug("== inserting {} Key '{}' for {} ==".format(game.upper(), code, platform.upper())) c.execute("INSERT INTO keys(description, key, platform, game, redeemed) " "VAlUES (?,?,?,?,0)", (desc, code, platform, game)) conn.commit()
def __login(self, user, pw): """Login with user/pw""" the_url = "{}/home".format(base_url) status_code, token = self.__get_token(the_url) if not token: return None login_data = {"authenticity_token": token, "user[email]": user, "user[password]": pw} headers = {"Referer": the_url} r = self.client.post("{}/sessions".format(base_url), # r = self.client.post("{}/sessions".format("http://127.0.0.1:8000"), data=login_data, headers=headers) _L.debug("{} {} {}".format(r.request.method, r.url, r.status_code)) return r
def redeem(self, code): found, status_code, form_data = self.__get_redemption_form(code) # the expired message comes from even wanting to redeem if not found: # entered key was invalid if status_code == 500: return Status.INVALID # entered key expired by now if "expired" in form_data: return Status.EXPIRED # unknown _L.error(form_data) return Status.UNKNOWN # the key is valid and all. status, result = self.__redeem_form(form_data) self.last_status = status _L.debug("{}: {}".format(Status(status), result)) return status
if __name__ == "__main__": import os # only print license text on first use if not os.path.exists(os.path.join(DIRNAME, ".cookies.save")): print(LICENSE_TEXT) # build argument parser parser = setup_argparser() args = parser.parse_args() _L.setLevel(INFO) if args.verbose: _L.setLevel(DEBUG) _L.debug("Debug mode on") # always execute at least once main(args) # scheduling will start after first trigger (so in an hour..) if args.schedule: _L.info("Scheduling to run once an hour") from apscheduler.schedulers.blocking import BlockingScheduler scheduler = BlockingScheduler() # fire every 1h5m # (5min safe margin because it somtimes fires a few seconds too early) scheduler.add_job(main, "interval", args=(args, ), hours=1, minutes=5) print('Press Ctrl+{} to exit'.format('Break' if os.name == 'nt' else 'C'))