Move class inside Classes folder and rename all file with the same name of the Class - Nothing work yet

This commit is contained in:
Alessandro Maggio
2021-01-12 19:17:06 +01:00
parent 143cb0ffe1
commit 58fb7a6f43
12 changed files with 368 additions and 217 deletions

138
.gitignore vendored Normal file
View File

@@ -0,0 +1,138 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/

13
classes/PubsubTopic.py Normal file
View File

@@ -0,0 +1,13 @@
class PubsubTopic:
def __init__(self, topic, channel_login=None):
self.topic = topic
self.channel_login = channel_login
def is_user_topic(self):
return self.channel_login is None
def __str__(self):
if self.is_user_topic():
return f"{self.topic}.{get_user_id()}"
else:
return f"{self.topic}.{get_channel_id(self.channel_login)}"

10
classes/Raid.py Normal file
View File

@@ -0,0 +1,10 @@
class Raid:
def __init__(self, raid_id, target_login):
self.raid_id = raid_id
self.target_login = target_login
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.raid_id == other.raid_id
else:
return False

4
classes/RequestInfo.py Normal file
View File

@@ -0,0 +1,4 @@
class RequestInfo:
def __init__(self, url, payload):
self.url = url
self.payload = payload

143
classes/TwitchLogin.py Normal file
View File

@@ -0,0 +1,143 @@
# Based on https://github.com/derrod/twl.py
# Original Copyright (c) 2020 Rodney
# The MIT License (MIT)
import logging
import getpass
import browser_cookie3
import requests
from cookies import save_cookies_to_file
from twitch_data import get_client_id
logger = logging.getLogger(__name__)
class TwitchLogin:
def __init__(self):
self.token = None
self.login_check_result = False
self.session = requests.session()
self.session.headers.update({'Client-ID': get_client_id()})
self.username = None
self.user_id = None
self.email = None
def login_flow(self):
logger.info("You'll have to login to Twitch!")
post_data = {
'client_id': get_client_id(),
'undelete_user': False,
'remember_me': True
}
use_backup_flow = False
while True:
self.username = input('Enter Twitch username: ')
password = getpass.getpass('Enter Twitch password: ')
post_data['username'] = self.username
post_data['password'] = password
while True:
# Try login without 2FA
login_response = self.send_login_request(post_data)
if 'captcha_proof' in login_response:
post_data['captcha'] = dict(proof=login_response['captcha_proof'])
if 'error_code' in login_response:
err_code = login_response['error_code']
if err_code == 3011 or err_code == 3012: # missing 2fa token
if err_code == 3011:
logger.info('Two factor authentication enabled, please enter token below.')
else:
logger.info('Invalid two factor token, please try again.')
twofa = input('2FA token: ')
post_data['authy_token'] = twofa.strip()
continue
elif err_code == 3022 or err_code == 3023: # missing 2fa token
if err_code == 3022:
logger.info('Login Verification code required.')
self.email = login_response['obscured_email']
else:
logger.info('Invalid Login Verification code entered, please try again.')
twofa = input(f'Please enter the 6-digit code sent to {self.email}: ')
post_data['twitchguard_code'] = twofa.strip()
continue
elif err_code == 3001: # invalid password
logger.info('Invalid username or password, please try again.')
break
elif err_code == 1000:
logger.info('Console login unavailable (CAPTCHA solving required).')
use_backup_flow = True
break
else:
logger.info(f'Unknown error: {login_response}')
raise NotImplementedError(f'Unknown TwitchAPI error code: {err_code}')
if 'access_token' in login_response:
self.set_token(login_response['access_token'])
return self.check_login()
if use_backup_flow:
break
if use_backup_flow:
self.set_token(self.login_flow_backup())
return self.check_login()
return False
def set_token(self, new_token):
self.token = new_token
self.session.headers.update({'Authorization': f'Bearer {self.token}'})
def send_login_request(self, json_data):
r = self.session.post('https://passport.twitch.tv/login', json=json_data)
j = r.json()
return j
def login_flow_backup(self):
"""Backup OAuth login flow in case manual captcha solving is required"""
browser = input("What browser do you use? Chrome (1), Firefox (2), Other (3): ").strip()
if browser not in ("1", "2"):
logger.info("Your browser is unsupported, sorry.")
return None
input("Please login inside your browser of choice (NOT incognito mode) and press Enter...")
logger.info("Loading cookies saved on your computer...")
twitch_domain = ".twitch.tv"
if browser == "1": # chrome
cookie_jar = browser_cookie3.chrome(domain_name=twitch_domain)
else:
cookie_jar = browser_cookie3.firefox(domain_name=twitch_domain)
cookies_dict = requests.utils.dict_from_cookiejar(cookie_jar)
self.username = cookies_dict.get("login")
return cookies_dict.get("auth-token")
def check_login(self):
if self.login_check_result:
return self.login_check_result
if self.token is None:
return False
r = self.session.get(f'https://api.twitch.tv/helix/users?login={self.username}')
response = r.json()
if "data" in response:
self.login_check_result = True
self.user_id = response["data"][0]["id"]
return self.login_check_result
def save_cookies(self):
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
save_cookies_to_file(cookies_dict)

56
classes/WebsocketsPool.py Normal file
View File

@@ -0,0 +1,56 @@
import logging
import threading
import websocket
import time
logger = logging.getLogger(__name__)
class WebsocketsPool: # you can't listen for more than 50 topics at once
def __init__(self):
self.free_websocket = None
def submit(self, pubsub_topic):
if self.free_websocket is None or len(self.free_websocket.topics) >= 50:
self.create_new_websocket()
self.free_websocket.topics.append(pubsub_topic)
if not self.free_websocket.is_opened:
self.free_websocket.pending_topics.append(pubsub_topic)
else:
listen_for_topic(self.free_websocket, pubsub_topic)
def create_new_websocket(self):
self.free_websocket = websocket.WebSocketApp("wss://pubsub-edge.twitch.tv/v1",
on_message=on_message,
on_open=WebsocketsPool.on_open,
on_close=WebsocketsPool.handle_websocket_reconnection
)
self.free_websocket.parent_pool = self
self.free_websocket.is_closed = False
self.free_websocket.is_opened = False
self.free_websocket.topics = []
self.free_websocket.pending_topics = []
threading.Thread(target=lambda: self.free_websocket.run_forever()).start()
@staticmethod
def on_open(ws):
def run():
ws.is_opened = True
ping(ws)
for topic in ws.pending_topics:
listen_for_topic(ws, topic)
while not ws.is_closed:
ping(ws)
time.sleep(30)
threading.Thread(target=run).start()
@staticmethod
def handle_websocket_reconnection(ws):
ws.is_closed = True
logger.info("Reconnecting to Twitch PubSub server in 30 seconds")
time.sleep(30)
self = ws.parent_pool
if self.free_websocket == ws:
self.free_websocket = None
for topic in ws.topics:
self.submit(topic)

0
classes/__init__.py Normal file
View File

142
login.py
View File

@@ -1,15 +1,6 @@
import getpass
import os
import browser_cookie3
import requests
from cookies import get_cookies_path, save_cookies_to_file
from twitch_data import get_client_id
# Based on https://github.com/derrod/twl.py
# Original Copyright (c) 2020 Rodney
# The MIT License (MIT)
from cookies import get_cookies_path
from classes.TwitchLogin import TwitchLogin
def check_login():
if not os.path.exists(get_cookies_path()):
@@ -23,132 +14,3 @@ def check_login():
return True
class TwitchLogin:
def __init__(self):
self.token = None
self.login_check_result = False
self.session = requests.session()
self.session.headers.update({'Client-ID': get_client_id()})
self.username = None
self.user_id = None
self.email = None
def login_flow(self):
print("You'll have to login to Twitch!")
post_data = {
'client_id': get_client_id(),
'undelete_user': False,
'remember_me': True
}
use_backup_flow = False
while True:
self.username = input('Enter Twitch username: ')
password = getpass.getpass('Enter Twitch password: ')
post_data['username'] = self.username
post_data['password'] = password
while True:
# Try login without 2FA
login_response = self.send_login_request(post_data)
if 'captcha_proof' in login_response:
post_data['captcha'] = dict(proof=login_response['captcha_proof'])
if 'error_code' in login_response:
err_code = login_response['error_code']
if err_code == 3011 or err_code == 3012: # missing 2fa token
if err_code == 3011:
print('Two factor authentication enabled, please enter token below.')
else:
print('Invalid two factor token, please try again.')
twofa = input('2FA token: ')
post_data['authy_token'] = twofa.strip()
continue
elif err_code == 3022 or err_code == 3023: # missing 2fa token
if err_code == 3022:
print('Login Verification code required.')
self.email = login_response['obscured_email']
else:
print('Invalid Login Verification code entered, please try again.')
twofa = input(f'Please enter the 6-digit code sent to {self.email}: ')
post_data['twitchguard_code'] = twofa.strip()
continue
elif err_code == 3001: # invalid password
print('Invalid username or password, please try again.')
break
elif err_code == 1000:
print('Console login unavailable (CAPTCHA solving required).')
use_backup_flow = True
break
else:
print(f'Unknown error: {login_response}')
raise NotImplementedError(f'Unknown TwitchAPI error code: {err_code}')
if 'access_token' in login_response:
self.set_token(login_response['access_token'])
return self.check_login()
if use_backup_flow:
break
if use_backup_flow:
self.set_token(self.login_flow_backup())
return self.check_login()
return False
def set_token(self, new_token):
self.token = new_token
self.session.headers.update({'Authorization': f'Bearer {self.token}'})
def send_login_request(self, json_data):
r = self.session.post('https://passport.twitch.tv/login', json=json_data)
j = r.json()
return j
def login_flow_backup(self):
"""Backup OAuth login flow in case manual captcha solving is required"""
browser = input("What browser do you use? Chrome (1), Firefox (2), Other (3): ").strip()
if browser not in ("1", "2"):
print("Your browser is unsupported, sorry.")
return None
input("Please login inside your browser of choice (NOT incognito mode) and press Enter...")
print("Loading cookies saved on your computer...")
twitch_domain = ".twitch.tv"
if browser == "1": # chrome
cookie_jar = browser_cookie3.chrome(domain_name=twitch_domain)
else:
cookie_jar = browser_cookie3.firefox(domain_name=twitch_domain)
cookies_dict = requests.utils.dict_from_cookiejar(cookie_jar)
self.username = cookies_dict.get("login")
return cookies_dict.get("auth-token")
def check_login(self):
if self.login_check_result:
return self.login_check_result
if self.token is None:
return False
r = self.session.get(f'https://api.twitch.tv/helix/users?login={self.username}')
response = r.json()
if "data" in response:
self.login_check_result = True
self.user_id = response["data"][0]["id"]
return self.login_check_result
def save_cookies(self):
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
save_cookies_to_file(cookies_dict)

View File

@@ -7,6 +7,8 @@ from claim_bonus import claim_channel_points_bonus
from raid import update_raid, Raid
from twitch_data import *
from classes.WebsocketsPool import WebsocketsPool
from classes.PubsubTopic import PubsubTopic
# For documentation on Twitch PubSub API, see https://dev.twitch.tv/docs/pubsub
@@ -107,69 +109,6 @@ def get_reason_name(code):
return code.replace("_", " ").replace("CLAIM", "bonus claimed").lower()
class PubsubTopic:
def __init__(self, topic, channel_login=None):
self.topic = topic
self.channel_login = channel_login
def is_user_topic(self):
return self.channel_login is None
def __str__(self):
if self.is_user_topic():
return f"{self.topic}.{get_user_id()}"
else:
return f"{self.topic}.{get_channel_id(self.channel_login)}"
class WebsocketsPool: # you can't listen for more than 50 topics at once
def __init__(self):
self.free_websocket = None
def submit(self, pubsub_topic):
if self.free_websocket is None or len(self.free_websocket.topics) >= 50:
self.create_new_websocket()
self.free_websocket.topics.append(pubsub_topic)
if not self.free_websocket.is_opened:
self.free_websocket.pending_topics.append(pubsub_topic)
else:
listen_for_topic(self.free_websocket, pubsub_topic)
def create_new_websocket(self):
self.free_websocket = websocket.WebSocketApp("wss://pubsub-edge.twitch.tv/v1",
on_message=on_message, on_open=WebsocketsPool.on_open, on_close=WebsocketsPool.handle_websocket_reconnection)
self.free_websocket.parent_pool = self
self.free_websocket.is_closed = False
self.free_websocket.is_opened = False
self.free_websocket.topics = []
self.free_websocket.pending_topics = []
threading.Thread(target=lambda: self.free_websocket.run_forever()).start()
@staticmethod
def on_open(ws):
def run():
ws.is_opened = True
ping(ws)
for topic in ws.pending_topics:
listen_for_topic(ws, topic)
while not ws.is_closed:
ping(ws)
time.sleep(30)
threading.Thread(target=run).start()
@staticmethod
def handle_websocket_reconnection(ws):
ws.is_closed = True
print("Reconnecting to Twitch PubSub server in 30 seconds")
time.sleep(30)
self = ws.parent_pool
if self.free_websocket == ws:
self.free_websocket = None
for topic in ws.topics:
self.submit(topic)
def listen_for_topic(ws, topic):
data = {"topics": [str(topic)]}
if topic.is_user_topic:
@@ -187,7 +126,6 @@ def send(ws, request):
request_str = json.dumps(request, separators=(',', ':'))
ws.send(request_str)
# https://en.wikipedia.org/wiki/Cryptographic_nonce
def create_nonce(length=30):
nonce = ""

View File

@@ -1,16 +1,7 @@
from claim_bonus import post_gql_request
class Raid:
def __init__(self, raid_id, target_login):
self.raid_id = raid_id
self.target_login = target_login
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.raid_id == other.raid_id
else:
return False
raid_id_for_streamer = {}

View File

@@ -12,10 +12,6 @@ from twitch_data import get_streamer_url, USER_AGENT
# "minute-watched" requests are what Twitch uses to know if you're watching a stream, and to grant you channel points.
class RequestInfo:
def __init__(self, url, payload):
self.url = url
self.payload = payload
# "minute-watched" request for each streamer