Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: new CLIENT_ID (twitch app?) #1

Merged
merged 11 commits into from
Oct 13, 2022
89 changes: 86 additions & 3 deletions TwitchChannelPointsMiner/classes/Twitch.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import os
import random
import re
import string
import time
from datetime import datetime
from pathlib import Path
from secrets import token_hex
from secrets import choice, token_hex

import requests

Expand All @@ -28,7 +30,12 @@
Settings,
)
from TwitchChannelPointsMiner.classes.TwitchLogin import TwitchLogin
from TwitchChannelPointsMiner.constants import CLIENT_ID, GQLOperations
from TwitchChannelPointsMiner.constants import (
CLIENT_ID,
CLIENT_VERSION,
URL,
GQLOperations,
)
from TwitchChannelPointsMiner.utils import (
_millify,
create_chunks,
Expand All @@ -39,7 +46,18 @@


class Twitch(object):
__slots__ = ["cookies_file", "user_agent", "twitch_login", "running"]
__slots__ = [
"cookies_file",
"user_agent",
"twitch_login",
"running",
"device_id",
"integrity",
"integrity_expire",
"client_session",
"client_version",
"twilight_build_id_pattern",
]

def __init__(self, username, user_agent, password=None):
cookies_path = os.path.join(Path().absolute(), "cookies")
Expand All @@ -50,6 +68,16 @@ def __init__(self, username, user_agent, password=None):
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.integrity = None
self.integrity_expire = 0
self.client_session = token_hex(16)
self.client_version = CLIENT_VERSION
self.twilight_build_id_pattern = re.compile(
r"window\.__twilightBuildID=\"([0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12})\";"
)

def login(self):
if os.path.isfile(self.cookies_file) is False:
Expand Down Expand Up @@ -231,7 +259,11 @@ def post_gql_request(self, json_data):
headers={
"Authorization": f"OAuth {self.twitch_login.get_auth_token()}",
"Client-Id": CLIENT_ID,
"Client-Integrity": self.post_integrity(),
"Client-Session-Id": self.client_session,
"Client-Version": self.update_client_version(),
"User-Agent": self.user_agent,
"X-Device-Id": self.device_id,
},
)
logger.debug(
Expand All @@ -244,6 +276,57 @@ def post_gql_request(self, json_data):
)
return {}

# Request for Integrity Token
# Twitch needs Authorization, Client-Id, X-Device-Id to generate JWT which is used for authorize gql requests
# Regenerate Integrity Token 5 minutes before expire
def post_integrity(self):
if (
self.integrity_expire - datetime.now().timestamp() * 1000 > 5 * 60 * 1000
and self.integrity is not None
):
return self.integrity
try:
response = requests.post(
GQLOperations.integrity_url,
json={},
headers={
"Authorization": f"OAuth {self.twitch_login.get_auth_token()}",
"Client-Id": CLIENT_ID,
"Client-Session-Id": self.client_session,
"Client-Version": self.update_client_version(),
"User-Agent": self.user_agent,
"X-Device-Id": self.device_id,
},
)
logger.debug(
f"Data: [], Status code: {response.status_code}, Content: {response.text}"
)
self.integrity = response.json().get("token", None)
self.integrity_expire = response.json().get("expiration", 0)
return self.integrity
except requests.exceptions.RequestException as e:
logger.error(f"Error with post_integrity: {e}")
return self.integrity

def update_client_version(self):
try:
response = requests.get(URL)
if response.status_code != 200:
logger.debug(
f"Error with update_client_version: {response.status_code}"
)
return self.client_version
matcher = re.search(self.twilight_build_id_pattern, response.text)
if not matcher:
logger.debug("Error with update_client_version: no match")
return self.client_version
self.client_version = matcher.group(1)
logger.debug(f"Client version: {self.client_version}")
return self.client_version
except requests.exceptions.RequestException as e:
logger.error(f"Error with update_client_version: {e}")
return self.client_version

def send_minute_watched_events(self, streamers, priority, chunk_size=3):
while self.running:
try:
Expand Down
4 changes: 3 additions & 1 deletion TwitchChannelPointsMiner/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
IRC = "irc.chat.twitch.tv"
IRC_PORT = 6667
WEBSOCKET = "wss://pubsub-edge.twitch.tv/v1"
CLIENT_ID = "kimne78kx3ncx6brgo4mv6wki5h1ko"
CLIENT_ID = "kd1unb4b3q4t58fwlpcbzcbnm76a8fp"
DROP_ID = "c2542d6d-cd10-4532-919b-3d19f30a768b"
CLIENT_VERSION = "32d439b2-bd5b-4e35-b82a-fae10b04da70"

USER_AGENTS = {
"Windows": {
Expand All @@ -26,6 +27,7 @@

class GQLOperations:
url = "https://gql.twitch.tv/gql"
integrity_url = "https://gql.twitch.tv/integrity"
WithIsStreamLiveQuery = {
"operationName": "WithIsStreamLiveQuery",
"extensions": {
Expand Down