Skip to content

Commit 31b321d

Browse files
committed
Initial commit
0 parents  commit 31b321d

7 files changed

Lines changed: 227 additions & 0 deletions

File tree

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.idea/
2+
config.json
3+
OverwatchTokenFarmer.spec
4+
5+
__pycache__/
6+
build/
7+
dist/
8+
venv/

.scripts/build.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
start cmd /k "..\venv\Scripts\activate&cd ..&pyinstaller --noconfirm --name OverwatchTokenFarmer main.py"

.scripts/start.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
start cmd /k "..\venv\Scripts\activate&cd ..&title Overwatch League Token Farmer&python main.py"

.scripts/venv.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
start cmd /k "..\venv\Scripts\activate&cd ..&title Venv"

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Overwatch League Token Farmer Bot
2+
This is a command line bot which "watches" league streams for you, 24/7, without the need to worry about missing some.
3+
**No password or other sort of authentication needed. Just your username.**
4+
5+
![demo](https://i.ibb.co/P90V5pk/image.png)
6+
7+
## Installation
8+
1. Download zip archive from [releases page](https://github.com/ucarno/ow-league-tokens/releases).
9+
2. Extract `OverwatchTokenFarmer` directory wherever you want.
10+
3. Optionally create desktop link to executable.
11+
12+
## Usage
13+
1. Open `OverwatchTokenFarmer.exe` located in app directory.
14+
2. Write your username and press Enter.
15+
3. If there are more than one person with your username, select right one.
16+
4. Done! Bot is now working and next time it will remember your username.
17+
18+
## Building executable
19+
* Install necessary libs from `requirements.txt`
20+
* Build using command `pyinstaller --noconfirm --name OverwatchTokenFarmer main.py`

main.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import json
2+
from datetime import datetime
3+
from threading import Thread
4+
from time import sleep
5+
6+
import requests
7+
from colorama import Fore, init
8+
9+
10+
LEAGUE_URL = 'https://overwatchleague.com/en-us/'
11+
TRACK_URL = 'https://wzavfvwgfk.execute-api.us-east-2.amazonaws.com/production/v2/sentinel-tracking/owl'
12+
USERS_API_URL = 'https://playoverwatch.com/en-us/search/account-by-name/%s/'
13+
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' \
14+
'(KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36'
15+
HEADERS = {
16+
'accept-language': 'en-US,en;q=1.0',
17+
'origin': 'https://overwatchleague.com',
18+
'referer': 'https://overwatchleague.com/',
19+
'x-origin': 'overwatchleague.com',
20+
'user-agent': USER_AGENT
21+
}
22+
23+
24+
success = Fore.GREEN
25+
failure = Fore.RED
26+
27+
28+
class Farmer:
29+
def __init__(self, account_id: int):
30+
self.account_id = int(account_id)
31+
32+
self.is_live = False
33+
self.video_id = None
34+
self.entry_id = None
35+
36+
self._continue = True
37+
38+
def updater_loop(self):
39+
while True:
40+
sleep(3*60)
41+
Thread(target=self.update_data).start()
42+
43+
def tracker_loop(self):
44+
while True:
45+
if not self._continue:
46+
sleep(3*60)
47+
self._continue = True
48+
if self.is_live:
49+
Thread(target=self.track_watching).start()
50+
sleep(1*60)
51+
52+
def start(self):
53+
self.log(f'Farmer started. Setting stream status to {self.status}.')
54+
self.update_data()
55+
updater_thread = Thread(target=self.updater_loop)
56+
tracker_thread = Thread(target=self.tracker_loop)
57+
58+
updater_thread.start()
59+
sleep(5)
60+
tracker_thread.start()
61+
62+
@property
63+
def status(self) -> str:
64+
status = ((success + 'Live') if self.is_live else (failure + 'Not Live')) + Fore.RESET
65+
return status
66+
67+
@staticmethod
68+
def get_next_data():
69+
html = requests.get(LEAGUE_URL, headers=HEADERS).text
70+
json_text = (
71+
html
72+
.split('<script id="__NEXT_DATA__" type="application/json">')[1]
73+
.split('</script>')[0]
74+
)
75+
return json.loads(json_text)
76+
77+
def update_data(self):
78+
self.log('Updating stream status...')
79+
80+
data = self.get_next_data()
81+
data_player = [i['videoPlayer'] for i in data['props']['pageProps']['blocks'] if i.get('videoPlayer')][0]
82+
# data_match = [i['matchTicker'] for i in data['props']['pageProps']['blocks'] if i.get('matchTicker')][0]
83+
84+
was_live = self.is_live
85+
self.entry_id = data_player['uid']
86+
87+
# try:
88+
# self.video_id = data_player['video']['id']
89+
# self.is_live = data_match['status'] == 'IN_PROGRESS' or data_match['timeToMatch'] == 0
90+
# except (KeyError, TypeError):
91+
# self.video_id = None
92+
# self.is_live = False
93+
94+
try:
95+
self.video_id = data_player['video']['id']
96+
self.is_live = bool(self.video_id)
97+
except (KeyError, TypeError):
98+
self.video_id = None
99+
self.is_live = False
100+
101+
if was_live == self.is_live:
102+
self.log(f'Nothing changed. Stream is still {self.status}.')
103+
else:
104+
self.log(f'Stream status changed. Stream is now {self.status}.')
105+
106+
def get_request_payload(self):
107+
return {
108+
'accountId': str(self.account_id),
109+
'entryId': self.entry_id,
110+
'liveTest': False,
111+
'locale': 'en-us',
112+
'type': 'video_player',
113+
'videoId': self.video_id,
114+
}
115+
116+
def track_watching(self):
117+
self.log('Sending time-tracking request...')
118+
post = requests.post(
119+
url=TRACK_URL,
120+
headers=HEADERS,
121+
json=self.get_request_payload()
122+
)
123+
options = requests.options(
124+
url=TRACK_URL,
125+
headers=HEADERS
126+
)
127+
128+
try:
129+
response = json.loads(post.text)
130+
self._continue = response['data']['continueTracking']
131+
except (KeyError, TypeError):
132+
self._continue = False
133+
134+
if post.status_code == 200:
135+
self.log(f'{success}Success.{Fore.RESET} Time successfully tracked!')
136+
else:
137+
self.log(f'{failure}Failure.{Fore.RESET} Time-tracking request was unsuccessful.')
138+
139+
@staticmethod
140+
def log(text: str):
141+
time = datetime.now().strftime('%H:%M:%S')
142+
print(f'[{time}] {text}')
143+
144+
145+
if __name__ == '__main__':
146+
init()
147+
148+
try:
149+
config = json.loads(open('config.json', 'r', encoding='utf-8').read())
150+
except FileNotFoundError:
151+
config = {'account_id': None}
152+
153+
if not config['account_id']:
154+
print('It looks like it is the first time you launch this program!')
155+
while True:
156+
print(f'What is your username? (for example {Fore.YELLOW}myUsername{Fore.RESET} '
157+
f'or {Fore.YELLOW}myUsername#1234{Fore.RESET})')
158+
username = input(' > ')
159+
if not username:
160+
print(Fore.RED + 'Incorrect username.', end='\n\n')
161+
continue
162+
163+
url = USERS_API_URL % (username.replace('#', '%23'),)
164+
users = json.loads(requests.get(url).text)
165+
if not users:
166+
print(Fore.RED + 'Users with that username not found.' + Fore.RESET, end='\n\n')
167+
continue
168+
169+
if len(users) == 1:
170+
user_dict = users[0]
171+
else:
172+
print(f'Found {len(users)} users with that username. You are..?')
173+
for index, item in enumerate(users):
174+
print(f' {index+1}. {item["name"]} (level: {item["playerLevel"]}, platform {item["platform"].upper()})')
175+
while True:
176+
number = input(' > ')
177+
if not number.isdigit() or int(number)-1 not in range(len(users)):
178+
print(f'{Fore.RED}Invalid number.{Fore.RESET}')
179+
continue
180+
user_dict = users[int(number)-1]
181+
break
182+
183+
aid = user_dict['id']
184+
config['account_id'] = aid
185+
with open('config.json', 'w+', encoding='utf-8') as f:
186+
f.write(json.dumps(config))
187+
f.close()
188+
189+
print(success + 'Configuration successful!' + Fore.RESET, end='\n\n')
190+
break
191+
192+
farmer = Farmer(**config)
193+
farmer.start()

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
requests
2+
colorama
3+
pyinstaller

0 commit comments

Comments
 (0)