Skip to content

Commit df5602e

Browse files
authored
Merge pull request Tkd-Alex#45 from rdavydov/mobile-token
new UA, login fix (only console)
2 parents c3bea5c + bf4b8b8 commit df5602e

File tree

7 files changed

+52
-28
lines changed

7 files changed

+52
-28
lines changed

TwitchChannelPointsMiner/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
2-
__version__ = "1.4.1"
2+
__version__ = "1.5.0"
33
from .TwitchChannelPointsMiner import TwitchChannelPointsMiner
44

55
__all__ = [

TwitchChannelPointsMiner/classes/Twitch.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ def __init__(self, username, user_agent, password=None):
6767
Path(cookies_path).mkdir(parents=True, exist_ok=True)
6868
self.cookies_file = os.path.join(cookies_path, f"{username}.pkl")
6969
self.user_agent = user_agent
70-
self.twitch_login = TwitchLogin(
71-
CLIENT_ID, username, self.user_agent, password=password
72-
)
73-
self.running = True
7470
self.device_id = "".join(
7571
choice(string.ascii_letters + string.digits) for _ in range(32)
7672
)
73+
self.twitch_login = TwitchLogin(
74+
CLIENT_ID, self.device_id, username, self.user_agent, password=password
75+
)
76+
self.running = True
7777
self.integrity = None
7878
self.integrity_expire = 0
7979
self.client_session = token_hex(16)
@@ -126,9 +126,14 @@ def update_stream(self, streamer):
126126

127127
def get_spade_url(self, streamer):
128128
try:
129-
headers = {"User-Agent": self.user_agent}
129+
# fixes AttributeError: 'NoneType' object has no attribute 'group'
130+
#headers = {"User-Agent": self.user_agent}
131+
from TwitchChannelPointsMiner.constants import USER_AGENTS
132+
headers = {"User-Agent": USER_AGENTS["Linux"]["FIREFOX"]}
133+
130134
main_page_request = requests.get(streamer.streamer_url, headers=headers)
131135
response = main_page_request.text
136+
#logger.info(response)
132137
regex_settings = "(https://static.twitchcdn.net/config/settings.*?js)"
133138
settings_url = re.search(regex_settings, response).group(1)
134139

TwitchChannelPointsMiner/classes/TwitchLogin.py

+33-12
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
logger = logging.getLogger(__name__)
2020

21-
def interceptor(request) -> str:
21+
"""def interceptor(request) -> str:
2222
if (
2323
request.method == 'POST'
2424
and request.url == 'https://passport.twitch.tv/protected_login'
@@ -29,11 +29,12 @@ def interceptor(request) -> str:
2929
data['client_id'] = CLIENT_ID
3030
request.body = json.dumps(data).encode('utf-8')
3131
del request.headers['Content-Length']
32-
request.headers['Content-Length'] = str(len(request.body))
32+
request.headers['Content-Length'] = str(len(request.body))"""
3333

3434
class TwitchLogin(object):
3535
__slots__ = [
3636
"client_id",
37+
"device_id",
3738
"token",
3839
"login_check_result",
3940
"session",
@@ -45,13 +46,14 @@ class TwitchLogin(object):
4546
"cookies"
4647
]
4748

48-
def __init__(self, client_id, username, user_agent, password=None):
49+
def __init__(self, client_id, device_id, username, user_agent, password=None):
4950
self.client_id = client_id
51+
self.device_id = device_id
5052
self.token = None
5153
self.login_check_result = False
5254
self.session = requests.session()
5355
self.session.headers.update(
54-
{"Client-ID": self.client_id, "User-Agent": user_agent}
56+
{ "Client-ID": self.client_id, "X-Device-Id": self.device_id, "User-Agent": user_agent }
5557
)
5658
self.username = username
5759
self.password = password
@@ -69,8 +71,8 @@ def login_flow(self):
6971
"remember_me": True,
7072
}
7173
# login-fix
72-
#use_backup_flow = False
73-
use_backup_flow = True
74+
use_backup_flow = False
75+
#use_backup_flow = True
7476

7577
for attempt in range(0, 25):
7678
password = (
@@ -131,8 +133,8 @@ def login_flow(self):
131133
# If the user didn't load the password from run.py we can just ask for it again.
132134
break
133135
# login-fix
134-
#elif err_code == 1000:
135-
elif err_code in [1000, 5022]:
136+
elif err_code == 1000:
137+
#elif err_code in [1000, 5022]:
136138
logger.info(
137139
"Console login unavailable (CAPTCHA solving required)."
138140
)
@@ -162,11 +164,18 @@ def set_token(self, new_token):
162164
self.session.headers.update({"Authorization": f"Bearer {self.token}"})
163165

164166
def send_login_request(self, json_data):
165-
response = self.session.post("https://passport.twitch.tv/protected_login", json=json_data)
167+
#response = self.session.post("https://passport.twitch.tv/protected_login", json=json_data)
168+
response = self.session.post("https://passport.twitch.tv/login", json=json_data, headers={
169+
'Accept': 'application/vnd.twitchtv.v3+json',
170+
'Accept-Encoding': 'gzip',
171+
'Accept-Language': 'en-US',
172+
'Content-Type': 'application/json; charset=UTF-8',
173+
'Host': 'passport.twitch.tv'
174+
},)
166175
return response.json()
167176

168177
def login_flow_backup(self, password = None):
169-
"""Backup OAuth Selenium login"""
178+
"""Backup OAuth Selenium login
170179
from undetected_chromedriver import ChromeOptions
171180
import seleniumwire.undetected_chromedriver.v2 as uc
172181
from selenium.webdriver.common.by import By
@@ -218,7 +227,9 @@ def login_flow_backup(self, password = None):
218227
logger.error("Couldn't extract login, probably bad cookies.")
219228
return False
220229
221-
return self.get_cookie_value("auth-token")
230+
return self.get_cookie_value("auth-token")"""
231+
logger.error("Backup login flow is not available. Use a VPN or wait a while to avoid the CAPTCHA.")
232+
return False
222233

223234
def check_login(self):
224235
if self.login_check_result:
@@ -230,7 +241,17 @@ def check_login(self):
230241
return self.login_check_result
231242

232243
def save_cookies(self, cookies_file):
233-
pickle.dump(self.cookies, open(cookies_file, "wb"))
244+
#pickle.dump(self.cookies, open(cookies_file, "wb"))
245+
# ^ only this line was needed with Selenium ^
246+
cookies_dict = self.session.cookies.get_dict()
247+
cookies_dict["auth-token"] = self.token
248+
if "persistent" not in cookies_dict: # saving user id cookies
249+
cookies_dict["persistent"] = self.user_id
250+
251+
self.cookies = []
252+
for cookie_name, value in cookies_dict.items():
253+
self.cookies.append({"name": cookie_name, "value": value})
254+
pickle.dump(self.cookies, open(cookies_file, "wb"))
234255

235256
def get_cookie_value(self, key):
236257
for cookie in self.cookies:

TwitchChannelPointsMiner/constants.py

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
"CHROME": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36",
2020
"FIREFOX": "Mozilla/5.0 (X11; Linux x86_64; rv:85.0) Gecko/20100101 Firefox/85.0",
2121
},
22+
"Android": {
23+
"App": "Dalvik/2.1.0 (Linux; U; Android 7.1.2; SM-G975N Build/N2G48C) tv.twitch.android.app/13.4.1/1304010"
24+
}
2225
}
2326

2427
BRANCH = "master"

TwitchChannelPointsMiner/utils.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,13 @@ def create_nonce(length=30) -> str:
5353
nonce += char
5454
return nonce
5555

56-
56+
# for mobile-token
5757
def get_user_agent(browser: str) -> str:
58-
try:
58+
"""try:
5959
return USER_AGENTS[platform.system()][browser]
6060
except KeyError:
61-
return USER_AGENTS["Linux"]["FIREFOX"]
61+
return USER_AGENTS["Linux"]["FIREFOX"]"""
62+
return USER_AGENTS["Android"]["App"]
6263

6364

6465
def remove_emoji(string: str) -> str:

requirements.txt

-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,3 @@ colorama
99
flask
1010
irc
1111
pandas
12-
selenium
13-
selenium-wire
14-
undetected_chromedriver

setup.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,7 @@ def read(fname):
3636
"colorama",
3737
"flask",
3838
"irc",
39-
"pandas",
40-
"selenium",
41-
"selenium-wire",
42-
"undetected_chromedriver"
39+
"pandas"
4340
],
4441
long_description=read("README.md"),
4542
long_description_content_type="text/markdown",

0 commit comments

Comments
 (0)