From 822e784e691fa54891778c20a2ff0330892d6db0 Mon Sep 17 00:00:00 2001 From: rdavydov <15850461+rdavydov@users.noreply.github.com> Date: Wed, 16 Nov 2022 14:17:30 +0300 Subject: [PATCH] new UA, login fix (only console) --- TwitchChannelPointsMiner/__init__.py | 2 +- TwitchChannelPointsMiner/classes/Twitch.py | 15 ++++--- .../classes/TwitchLogin.py | 45 ++++++++++++++----- TwitchChannelPointsMiner/constants.py | 3 ++ TwitchChannelPointsMiner/utils.py | 7 +-- requirements.txt | 3 -- setup.py | 5 +-- 7 files changed, 52 insertions(+), 28 deletions(-) diff --git a/TwitchChannelPointsMiner/__init__.py b/TwitchChannelPointsMiner/__init__.py index 64f7d1bb..6d732f0b 100644 --- a/TwitchChannelPointsMiner/__init__.py +++ b/TwitchChannelPointsMiner/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = "1.4.1" +__version__ = "1.5.0" from .TwitchChannelPointsMiner import TwitchChannelPointsMiner __all__ = [ diff --git a/TwitchChannelPointsMiner/classes/Twitch.py b/TwitchChannelPointsMiner/classes/Twitch.py index 745fda91..17d1873e 100644 --- a/TwitchChannelPointsMiner/classes/Twitch.py +++ b/TwitchChannelPointsMiner/classes/Twitch.py @@ -67,13 +67,13 @@ def __init__(self, username, user_agent, password=None): Path(cookies_path).mkdir(parents=True, exist_ok=True) self.cookies_file = os.path.join(cookies_path, f"{username}.pkl") self.user_agent = user_agent - self.twitch_login = TwitchLogin( - CLIENT_ID, username, self.user_agent, password=password - ) - self.running = True self.device_id = "".join( choice(string.ascii_letters + string.digits) for _ in range(32) ) + self.twitch_login = TwitchLogin( + CLIENT_ID, self.device_id, username, self.user_agent, password=password + ) + self.running = True self.integrity = None self.integrity_expire = 0 self.client_session = token_hex(16) @@ -126,9 +126,14 @@ def update_stream(self, streamer): def get_spade_url(self, streamer): try: - headers = {"User-Agent": self.user_agent} + # fixes AttributeError: 'NoneType' object has no attribute 'group' + #headers = {"User-Agent": self.user_agent} + from TwitchChannelPointsMiner.constants import USER_AGENTS + headers = {"User-Agent": USER_AGENTS["Linux"]["FIREFOX"]} + main_page_request = requests.get(streamer.streamer_url, headers=headers) response = main_page_request.text + #logger.info(response) regex_settings = "(https://static.twitchcdn.net/config/settings.*?js)" settings_url = re.search(regex_settings, response).group(1) diff --git a/TwitchChannelPointsMiner/classes/TwitchLogin.py b/TwitchChannelPointsMiner/classes/TwitchLogin.py index 4e7b2fd8..a53e5ef6 100644 --- a/TwitchChannelPointsMiner/classes/TwitchLogin.py +++ b/TwitchChannelPointsMiner/classes/TwitchLogin.py @@ -18,7 +18,7 @@ logger = logging.getLogger(__name__) -def interceptor(request) -> str: +"""def interceptor(request) -> str: if ( request.method == 'POST' and request.url == 'https://passport.twitch.tv/protected_login' @@ -29,11 +29,12 @@ def interceptor(request) -> str: data['client_id'] = CLIENT_ID request.body = json.dumps(data).encode('utf-8') del request.headers['Content-Length'] - request.headers['Content-Length'] = str(len(request.body)) + request.headers['Content-Length'] = str(len(request.body))""" class TwitchLogin(object): __slots__ = [ "client_id", + "device_id", "token", "login_check_result", "session", @@ -45,13 +46,14 @@ class TwitchLogin(object): "cookies" ] - def __init__(self, client_id, username, user_agent, password=None): + def __init__(self, client_id, device_id, username, user_agent, password=None): self.client_id = client_id + self.device_id = device_id self.token = None self.login_check_result = False self.session = requests.session() self.session.headers.update( - {"Client-ID": self.client_id, "User-Agent": user_agent} + { "Client-ID": self.client_id, "X-Device-Id": self.device_id, "User-Agent": user_agent } ) self.username = username self.password = password @@ -69,8 +71,8 @@ def login_flow(self): "remember_me": True, } # login-fix - #use_backup_flow = False - use_backup_flow = True + use_backup_flow = False + #use_backup_flow = True for attempt in range(0, 25): password = ( @@ -131,8 +133,8 @@ def login_flow(self): # If the user didn't load the password from run.py we can just ask for it again. break # login-fix - #elif err_code == 1000: - elif err_code in [1000, 5022]: + elif err_code == 1000: + #elif err_code in [1000, 5022]: logger.info( "Console login unavailable (CAPTCHA solving required)." ) @@ -162,11 +164,18 @@ def set_token(self, new_token): self.session.headers.update({"Authorization": f"Bearer {self.token}"}) def send_login_request(self, json_data): - response = self.session.post("https://passport.twitch.tv/protected_login", json=json_data) + #response = self.session.post("https://passport.twitch.tv/protected_login", json=json_data) + response = self.session.post("https://passport.twitch.tv/login", json=json_data, headers={ + 'Accept': 'application/vnd.twitchtv.v3+json', + 'Accept-Encoding': 'gzip', + 'Accept-Language': 'en-US', + 'Content-Type': 'application/json; charset=UTF-8', + 'Host': 'passport.twitch.tv' + },) return response.json() def login_flow_backup(self, password = None): - """Backup OAuth Selenium login""" + """Backup OAuth Selenium login from undetected_chromedriver import ChromeOptions import seleniumwire.undetected_chromedriver.v2 as uc from selenium.webdriver.common.by import By @@ -218,7 +227,9 @@ def login_flow_backup(self, password = None): logger.error("Couldn't extract login, probably bad cookies.") return False - return self.get_cookie_value("auth-token") + return self.get_cookie_value("auth-token")""" + logger.error("Backup login flow is not available. Use a VPN or wait a while to avoid the CAPTCHA.") + return False def check_login(self): if self.login_check_result: @@ -230,7 +241,17 @@ def check_login(self): return self.login_check_result def save_cookies(self, cookies_file): - pickle.dump(self.cookies, open(cookies_file, "wb")) + #pickle.dump(self.cookies, open(cookies_file, "wb")) + # ^ only this line was needed with Selenium ^ + cookies_dict = self.session.cookies.get_dict() + cookies_dict["auth-token"] = self.token + if "persistent" not in cookies_dict: # saving user id cookies + cookies_dict["persistent"] = self.user_id + + self.cookies = [] + for cookie_name, value in cookies_dict.items(): + self.cookies.append({"name": cookie_name, "value": value}) + pickle.dump(self.cookies, open(cookies_file, "wb")) def get_cookie_value(self, key): for cookie in self.cookies: diff --git a/TwitchChannelPointsMiner/constants.py b/TwitchChannelPointsMiner/constants.py index 26154b0b..e81966ca 100644 --- a/TwitchChannelPointsMiner/constants.py +++ b/TwitchChannelPointsMiner/constants.py @@ -19,6 +19,9 @@ "CHROME": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36", "FIREFOX": "Mozilla/5.0 (X11; Linux x86_64; rv:85.0) Gecko/20100101 Firefox/85.0", }, + "Android": { + "App": "Dalvik/2.1.0 (Linux; U; Android 7.1.2; SM-G975N Build/N2G48C) tv.twitch.android.app/13.4.1/1304010" + } } BRANCH = "master" diff --git a/TwitchChannelPointsMiner/utils.py b/TwitchChannelPointsMiner/utils.py index ea0b6515..fd88c5a5 100644 --- a/TwitchChannelPointsMiner/utils.py +++ b/TwitchChannelPointsMiner/utils.py @@ -53,12 +53,13 @@ def create_nonce(length=30) -> str: nonce += char return nonce - +# for mobile-token def get_user_agent(browser: str) -> str: - try: + """try: return USER_AGENTS[platform.system()][browser] except KeyError: - return USER_AGENTS["Linux"]["FIREFOX"] + return USER_AGENTS["Linux"]["FIREFOX"]""" + return USER_AGENTS["Android"]["App"] def remove_emoji(string: str) -> str: diff --git a/requirements.txt b/requirements.txt index 44859fce..5a011c2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,3 @@ colorama flask irc pandas -selenium -selenium-wire -undetected_chromedriver diff --git a/setup.py b/setup.py index 8413bf8a..30afcecd 100644 --- a/setup.py +++ b/setup.py @@ -36,10 +36,7 @@ def read(fname): "colorama", "flask", "irc", - "pandas", - "selenium", - "selenium-wire", - "undetected_chromedriver" + "pandas" ], long_description=read("README.md"), long_description_content_type="text/markdown",