· 6 years ago · Apr 10, 2020, 07:56 PM
1import configparser
2import operator
3import os
4import webbrowser
5from datetime import datetime, timedelta
6import time
7
8import pushbullet
9import pyperclip
10import pytesseract
11import requests
12import win32api
13import win32con
14import win32gui
15from PIL import ImageGrab, Image
16from playsound import playsound
17
18
19def Avg(lst: list):
20 return sum(lst) / len(lst)
21
22
23# noinspection PyShadowingNames
24def enum_cb(hwnd, results):
25 winlist.append((hwnd, win32gui.GetWindowText(hwnd)))
26
27
28# noinspection PyShadowingNames
29def write(message, add_time: bool = True, push: int = 0, push_now: bool = False, output: bool = True, ending: str = '\n'):
30 if add_time:
31 m = datetime.now().strftime('%H:%M:%S') + ': ' + str(message)
32 else:
33 m = message
34 if output:
35 print(m, end=ending)
36
37 if push >= 3:
38 global note
39 if message:
40 note = note + m + '\n'
41 if push_now:
42 device.push_note('CSGO AUTO ACCEPT', note)
43 note = ''
44
45
46# noinspection PyShadowingNames
47def click(x: int, y: int):
48 win32api.SetCursorPos((x, y))
49 win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, x, y, 0, 0)
50 win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, x, y, 0, 0)
51
52
53# noinspection PyShadowingNames
54def relate_list(l_org, l1, l2=None, relate: operator = operator.le):
55 if not l_org:
56 return False
57 truth_list, l3 = [], []
58 for i, val in enumerate(l1, start=0):
59 l3.append(relate(l_org[i], val))
60 truth_list.append(all(l3))
61 l3 = []
62 if l2:
63 for i, val in enumerate(l2, start=len(l1)):
64 l3.append(relate(l_org[i], val))
65 truth_list.append(all(l3))
66 return any(truth_list)
67
68
69# noinspection PyShadowingNames
70def color_average(image: Image, compare_list: list):
71 average = []
72 r, g, b = [], [], []
73 data = image.getdata()
74 for i in data:
75 r.append(i[0])
76 g.append(i[1])
77 b.append(i[2])
78
79 rgb = [Avg(r), Avg(g), Avg(b)] * int(len(compare_list) / 3)
80 for i, val in enumerate(compare_list, start=0):
81 average.append(val - rgb[i])
82 average = list(map(abs, average))
83
84 return average
85
86
87# noinspection PyShadowingNames
88def getScreenShot(window_id: int, area: tuple = (0, 0, 0, 0)):
89 area = list(area)
90 win32gui.ShowWindow(window_id, win32con.SW_MAXIMIZE)
91 scaled_area = [screen_width / 2560, screen_height / 1440]
92 scaled_area = 2 * scaled_area
93 for i, _ in enumerate(area[-2:], start=len(area) - 2):
94 area[i] += 1
95 for i, val in enumerate(area, start=0):
96 scaled_area[i] = scaled_area[i] * val
97 scaled_area = list(map(int, scaled_area))
98 image = ImageGrab.grab(scaled_area)
99 return image
100
101
102# noinspection PyShadowingNames
103def getAccountsFromCfg():
104 steam_ids = ''
105 for i in config.sections():
106 if i.startswith('Account'):
107 steam_id = config.get(i, 'Steam ID')
108 auth_code = config.get(i, 'Authentication Code')
109 match_token = config.get(i, 'Match Token')
110 steam_ids += steam_id + ','
111 accounts.append({'steam_id': steam_id, 'auth_code': auth_code, 'match_token': match_token})
112
113 steam_ids = steam_ids.lstrip(',').rstrip(',')
114 profiles = requests.get('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=' + cfg['steam_api_key'] + '&steamids=' + steam_ids).json()['response']['players']
115 for i in profiles:
116 for n in accounts:
117 if n['steam_id'] == i['steamid']:
118 n['name'] = i['personaname']
119 break
120
121
122# noinspection PyShadowingNames
123def getOldSharecodes(num: int = -1):
124 try:
125 last_game = open('last_game_' + accounts[current_account]['steam_id'] + '.txt', 'r')
126 games = last_game.readlines()
127 last_game.close()
128 except FileNotFoundError:
129 last_game = open('last_game_' + accounts[current_account]['steam_id'] + '.txt', 'w')
130 last_game.write(accounts[current_account]['match_token'] + '\n')
131 games = [accounts[current_account]['match_token']]
132 last_game.close()
133 last_game = open('last_game_' + accounts[current_account]['steam_id'] + '.txt', 'w')
134 games = games[-200:]
135 for i, val in enumerate(games):
136 games[i] = 'CSGO' + val.strip('\n').split('CSGO')[1]
137 last_game.write(games[i] + '\n')
138 last_game.close()
139 return games[num:]
140
141
142# noinspection PyShadowingNames
143def getNewCSGOMatches(game_id: str):
144 sharecodes = []
145 next_code = game_id
146 last_game = open('last_game_' + accounts[current_account]['steam_id'] + '.txt', 'a')
147 while next_code != 'n/a':
148 steam_url = 'https://api.steampowered.com/ICSGOPlayers_730/GetNextMatchSharingCode/v1?key=' + cfg['steam_api_key'] + '&steamid=' + accounts[current_account]['steam_id'] + '&steamidkey=' + accounts[current_account][
149 'auth_code'] + '&knowncode=' + game_id
150 try:
151 next_code = (requests.get(steam_url).json()['result']['nextcode'])
152 except KeyError:
153 write('WRONG Match Token, Authentication Code or Steam ID ')
154 return [game_id]
155
156 if next_code:
157 if next_code != 'n/a':
158 sharecodes.append(next_code)
159 game_id = next_code
160 last_game.write(next_code + '\n')
161 if sharecodes:
162 return sharecodes
163 else:
164 return [game_id]
165
166
167# noinspection PyShadowingNames
168def UpdateCSGOstats(sharecodes: list, num_completed: int = 1):
169 completed_games, not_completed_games, = [], []
170 for val in sharecodes:
171 response = requests.post('https://csgostats.gg/match/upload/ajax', data={'sharecode': val, 'index': '1'})
172 if response.json()['status'] == 'complete':
173 completed_games.append(response.json())
174 else:
175 not_completed_games.append(response.json())
176
177 queued_games = [game['data']['queue_pos'] for game in not_completed_games if game['status'] != 'error']
178 global retrying_games
179 retrying_games = []
180
181 if queued_games:
182 if queued_games[0] < cfg['max_queue_position']:
183 global time_table
184 retrying_games = [game['data']['sharecode'] for game in not_completed_games]
185 time_table['error_check_time'] = time.time()
186 for i, val in enumerate(queued_games):
187 write('#%s: in Queue #%s.' % (i + 1, val), add_time=False)
188
189 if len(not_completed_games) - len(queued_games) > 0:
190 write('An error occurred in %s game[s].' % (len(not_completed_games) - len(queued_games)), add_time=False)
191 retrying_games.append([game['data']['sharecode'] for game in not_completed_games if game['status'] == 'error'])
192
193 if completed_games:
194 for i in completed_games[num_completed * - 1:]:
195 sharecode = i['data']['sharecode']
196 game_url = i['data']['url']
197 info = ' '.join(i['data']['msg'].replace('-', '').replace('<br />', '. ').split('<')[0].rstrip(' ').split())
198 write('Sharecode: %s' % sharecode, add_time=False, push=push_urgency)
199 write('URL: %s' % game_url, add_time=False, push=push_urgency)
200 write('Status: %s.' % info, add_time=False, push=push_urgency)
201 pyperclip.copy(game_url)
202 write(None, add_time=False, push=push_urgency, push_now=True, output=False)
203
204
205# noinspection PyShadowingNames
206def Image_to_Text(image: Image, size: tuple, white_threshold: list, arg: str = ''):
207 image_data = image.getdata()
208 pixel_map, image_text = [], ''
209 for y in range(size[1]):
210 for x in range(size[0]):
211 if relate_list(image_data[y * size[0] + x], white_threshold, relate=operator.ge):
212 pixel_map.append((0, 0, 0))
213 else:
214 pixel_map.append((255, 255, 255))
215 temp_image = Image.new('RGB', (size[0], size[1]))
216 temp_image.putdata(pixel_map)
217 try:
218 image_text = pytesseract.image_to_string(temp_image, timeout=0.3, config=arg)
219 except RuntimeError as timeout_error:
220 pass
221 if image_text:
222 image_text = ' '.join(image_text.replace(': ', ':').split())
223 global truth_table
224 if truth_table['debugging']:
225 image.save(str(cfg['debug_path']) + '\\' + datetime.now().strftime('%H-%M-%S') + '_' + image_text.replace(':', '-') + '.png', format='PNG')
226 temp_image.save(str(cfg['debug_path']) + '\\' + datetime.now().strftime('%H-%M-%S') + '_' + image_text.replace(':', '-') + '_temp_.png', format='PNG')
227 return image_text
228 else:
229 return False
230
231
232def getCfgData():
233 try:
234 get_cfg = {'activate_script': int(config.get('HotKeys', 'Activate Script'), 16), 'activate_push_notification': int(config.get('HotKeys', 'Activate Push Notification'), 16),
235 'info_newest_match': int(config.get('HotKeys', 'Get Info on newest Match'), 16), 'info_multiple_matches': int(config.get('HotKeys', 'Get Info on multiple Matches'), 16),
236 'open_live_tab': int(config.get('HotKeys', 'Live Tab Key'), 16), 'switch_accounts': int(config.get('HotKeys', 'Switch accounts for csgostats.gg'), 16), 'stop_warmup_ocr': int(config.get('HotKeys', 'Stop Warmup OCR'), 16),
237 'end_script': int(config.get('HotKeys', 'End Script'), 16),
238 'screenshot_interval': config.getint('Screenshot', 'Interval'), 'debug_path': config.get('Screenshot', 'Debug Path'), 'steam_api_key': config.get('csgostats.gg', 'API Key'), 'last_x_matches': config.getint('csgostats.gg', 'Number of Requests'),
239 'completed_matches': config.getint('csgostats.gg', 'Completed Matches'), 'max_queue_position': config.getint('csgostats.gg', 'Auto-Retrying for queue position below'),
240 'auto_retry_interval': config.getint('csgostats.gg', 'Auto-Retrying-Interval'), 'pushbullet_device_name': config.get('Pushbullet', 'Device Name'), 'pushbullet_api_key': config.get('Pushbullet', 'API Key'),
241 'tesseract_path': config.get('Warmup', 'Tesseract Path'), 'warmup_test_interval': config.getint('Warmup', 'Test Interval'), 'warmup_push_interval': config.get('Warmup', 'Push Interval'),
242 'warmup_no_text_limit': config.getint('Warmup', 'No Text Limit')}
243 return get_cfg
244 # 'imgur_id': config.get('Imgur', 'Client ID'), 'imgur_secret': config.get('Imgur', 'Client Secret'),
245 except (configparser.NoOptionError, configparser.NoSectionError, ValueError):
246 write('ERROR IN CONFIG')
247 exit('CHECK FOR NEW CONFIG')
248
249
250# CONFIG HANDLING
251config = configparser.ConfigParser()
252config.read('config.ini')
253cfg = getCfgData()
254device = 0
255
256# ACCOUNT HANDLING, GETTING ACCOUNT NAME
257accounts, current_account = [], 0
258getAccountsFromCfg()
259
260# INITIALIZATION FOR getScreenShot
261screen_width, screen_height = win32api.GetSystemMetrics(0), win32api.GetSystemMetrics(1)
262toplist, winlist = [], []
263hwnd = 0
264
265# BOOLEAN INITIALIZATION
266truth_table = {'test_for_live_game': False, 'test_for_success': False, 'test_for_warmup': False, 'first_ocr': True, 'testing': False, 'debugging': False}
267
268# csgostats.gg VAR
269retrying_games = []
270
271# WARMUP DETECTION SETUP
272pytesseract.pytesseract.tesseract_cmd = cfg['tesseract_path']
273push_times, no_text_found, push_counter = [], 0, 0
274for i in cfg['warmup_push_interval'].split(','):
275 push_times.append(int(i))
276push_times.sort(reverse=True)
277join_warmup_time = push_times[0] + 1
278
279# PUSHBULLET VAR
280note = ''
281push_urgency = 0
282
283# INITIALIZATION OF TIME VARS
284time_table = {'screenshot_time': time.time(), 'error_check_time': time.time(), 'warmup_test_timer': time.time()}
285
286write('READY')
287write('Current account is: %s\n' % accounts[current_account]['name'], add_time=False)
288
289while True:
290 if win32api.GetAsyncKeyState(cfg['activate_script']) & 1: # F9 (ACTIVATE / DEACTIVATE SCRIPT)
291 truth_table['test_for_live_game'] = not truth_table['test_for_live_game']
292 write('TESTING: %s' % truth_table['test_for_live_game'])
293 if truth_table['test_for_live_game']:
294 playsound('sounds/activated_2.mp3')
295 time_searching = time.time()
296 else:
297 playsound('sounds/deactivated.mp3')
298
299 if win32api.GetAsyncKeyState(cfg['activate_push_notification']) & 1: # F8 (ACTIVATE / DEACTIVATE PUSH NOTIFICATION)
300 if not device:
301 try:
302 device = pushbullet.PushBullet(cfg['pushbullet_api_key']).get_device(cfg['pushbullet_device_name'])
303 except (pushbullet.errors.PushbulletError, pushbullet.errors.InvalidKeyError):
304 write('Pushbullet is wrongly configured.\nWrong API Key or DeviceName in config.ini')
305 if device:
306 push_urgency += 1
307 if push_urgency > 3:
308 push_urgency = 0
309 push_info = ['not active', 'only if accepted', 'all game status related information', 'all information (game status/csgostats.gg information)']
310 write('Pushing: %s' % push_info[push_urgency])
311
312 if win32api.GetAsyncKeyState(cfg['info_newest_match']) & 1: # F7 Key (UPLOAD NEWEST MATCH)
313 write('Uploading / Getting status on newest match')
314 UpdateCSGOstats(getNewCSGOMatches(getOldSharecodes()[0]))
315
316 if win32api.GetAsyncKeyState(cfg['info_multiple_matches']) & 1: # F6 Key (GET INFO ON LAST X MATCHES)
317 write('Getting Info from last %s matches' % cfg['last_x_matches'])
318 getNewCSGOMatches(getOldSharecodes()[0])
319 UpdateCSGOstats(getOldSharecodes(num=cfg['last_x_matches'] * -1), num_completed=cfg['completed_matches'])
320
321 if win32api.GetAsyncKeyState(cfg['open_live_tab']) & 1: # F13 Key (OPEN WEB BROWSER ON LIVE GAME TAB)
322 win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE)
323 webbrowser.open_new_tab('https://csgostats.gg/player/' + accounts[current_account]['steam_id'] + '#/live')
324 write('new tab opened', add_time=False)
325 time.sleep(0.5)
326 win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE)
327
328 if win32api.GetAsyncKeyState(cfg['switch_accounts']) & 1: # F15 (SWITCH ACCOUNTS)
329 current_account += 1
330 if current_account > len(accounts) - 1:
331 current_account = 0
332 write('current account is: %s' % accounts[current_account]['name'], add_time=False)
333
334 if win32api.GetAsyncKeyState(cfg['stop_warmup_ocr']) & 1: # ESC (STOP WARMUP OCR)
335 write('STOPPING WARMUP TIME FINDER!', add_time=False)
336 truth_table['test_for_warmup'] = False
337 no_text_found = 0
338 time_table['warmup_test_timer'] = time.time()
339
340 if win32api.GetAsyncKeyState(cfg['end_script']) & 1: # POS1 (END SCRIPT)
341 write('Exiting Script')
342 break
343
344 if retrying_games:
345 if time.time() - time_table['error_check_time'] > cfg['auto_retry_interval']:
346 time_table['error_check_time'] = time.time()
347 UpdateCSGOstats(retrying_games, num_completed=len(retrying_games))
348
349 winlist = []
350 win32gui.EnumWindows(enum_cb, toplist)
351 csgo = [(hwnd, title) for hwnd, title in winlist if 'counter-strike: global offensive' in title.lower()]
352
353 # ONLY CONTINUING IF CSGO IS RUNNING
354 if not csgo:
355 continue
356 hwnd = csgo[0][0]
357
358 # TESTING HERE
359 if win32api.GetAsyncKeyState(0x6F) & 1: # UNBOUND, TEST CODE
360 # write('Executing Debugging\n')
361 # truth_table['testing'] = not truth_table['testing']
362 truth_table['debugging'] = not truth_table['debugging']
363 write('DEBUGGING: %s\n' % truth_table['debugging'])
364
365 if truth_table['testing']:
366 # time_table['screenshot_time'] = time.time()
367 pass
368 # print('Took: %s ' % str(timedelta(milliseconds=int(time.time(*1000 - time_table['screenshot_time']*1000))))
369 # TESTING ENDS HERE
370
371 if truth_table['test_for_live_game']:
372 if time.time() - time_table['screenshot_time'] < cfg['screenshot_interval']:
373 continue
374 time_table['screenshot_time'] = time.time()
375 img = getScreenShot(hwnd, (1265, 760, 1295, 785))
376 if not img:
377 continue
378 accept_avg = color_average(img, [76, 176, 80, 90, 203, 95])
379
380 if relate_list(accept_avg, [1, 2, 1], l2=[1, 1, 2]):
381 write('Trying to Accept', push=push_urgency + 1)
382
383 truth_table['test_for_success'] = True
384 truth_table['test_for_live_game'] = False
385 accept_avg = []
386
387 for _ in range(5):
388 click(int(screen_width / 2), int(screen_height / 1.78))
389
390 write('Trying to catch a loading map')
391 playsound('sounds/accept_found.mp3')
392 time_table['screenshot_time'] = time.time()
393
394 if truth_table['test_for_success']:
395 if time.time() - time_table['screenshot_time'] < 40:
396 img = getScreenShot(hwnd, (2435, 65, 2555, 100))
397 not_searching_avg = color_average(img, [6, 10, 10])
398 searching_avg = color_average(img, [6, 163, 97, 4, 63, 35])
399
400 not_searching = relate_list(not_searching_avg, [2, 5, 5])
401 searching = relate_list(searching_avg, [2.7, 55, 35], l2=[1, 50, 35])
402
403 img = getScreenShot(hwnd, (467, 1409, 1300, 1417))
404 success_avg = color_average(img, [21, 123, 169])
405 success = relate_list(success_avg, [1, 8, 7])
406
407 if success:
408 write('Took %s since pressing accept.' % str(timedelta(seconds=int(time.time() - time_table['screenshot_time']))), add_time=False, push=push_urgency + 1)
409 write('Took %s since trying to find a game.' % str(timedelta(seconds=int(time.time() - time_searching))), add_time=False, push=push_urgency + 1)
410 write('Game should have started', push=push_urgency + 2, push_now=True)
411 truth_table['test_for_success'] = False
412 truth_table['test_for_warmup'] = True
413 playsound('sounds/done_testing.mp3')
414 time_table['warmup_test_timer'] = time.time()+5
415
416 if any([searching, not_searching]):
417 write('Took: %s ' % str(timedelta(seconds=int(time.time() - time_table['screenshot_time']))), add_time=False, push=push_urgency + 1)
418 write('Game doesnt seem to have started. Continuing to search for accept Button!', push=push_urgency + 1, push_now=True)
419 playsound('sounds/back_to_testing.mp3')
420 truth_table['test_for_success'] = False
421 truth_table['test_for_live_game'] = True
422
423 else:
424 write('40 Seconds after accept, did not find loading map nor searching queue')
425 truth_table['test_for_success'] = False
426 print(success_avg)
427 print(searching_avg)
428 print(not_searching_avg)
429 playsound('sounds/fail.mp3')
430 img.save(os.path.expanduser('~') + '\\Unknown Error.png')
431
432 if truth_table['test_for_warmup']:
433 for i in range(112, 136):
434 win32api.GetAsyncKeyState(i) & 1
435 while True:
436 keys = []
437 for i in range(112, 136):
438 keys.append(win32api.GetAsyncKeyState(i) & 1)
439 if any(keys):
440 print('')
441 write('Break from warmup-loop')
442 truth_table['test_for_warmup'] = False
443 truth_table['first_ocr'] = True
444 break
445
446 if time.time() - time_table['warmup_test_timer'] >= cfg['warmup_test_interval']:
447 img = getScreenShot(hwnd, (1036, 425, 1525, 456)) # 'WAITING FOR PLAYERS X:XX'
448 img_text = Image_to_Text(img, img.size, [225, 225, 225], arg='--psm 6')
449 time_table['warmup_test_timer'] = time.time()
450 if img_text:
451 time_left = img_text.split()[-1].split(':')
452 # write(img_text, add_time=False)
453 try:
454 time_left = int(time_left[0]) * 60 + int(time_left[1])
455 if truth_table['first_ocr']:
456 join_warmup_time = time_left
457 time_table['screenshot_time'] = time.time()
458 truth_table['first_ocr'] = False
459
460 except ValueError:
461 time_left = push_times[0] + 1
462
463 write('\rTime since start: %s - Time Difference: %s - Time left: %s' % (timedelta(seconds=int(time.time() - time_table['screenshot_time'])), time.strftime('%H:%M:%S', time.gmtime(join_warmup_time-time_left)), img_text), add_time=False, ending='')
464 # write('Time since start: %s, %s' % (timedelta(seconds=int(time.time( - time_table['screenshot_time'])), img_text), add_time=False)
465 if no_text_found > 0:
466 no_text_found -= 1
467
468 if time_left <= push_times[push_counter]:
469 push_counter += 1
470 write('Time since start: %s\nTime Difference: %s\nTime left: %s' % (timedelta(seconds=int(time.time() - time_table['screenshot_time'])), time.strftime('%H:%M:%S', time.gmtime(join_warmup_time-time_left)), img_text), add_time=True, push=push_urgency + 1, output=False, push_now=True)
471
472 else:
473 no_text_found += 1
474
475 if push_counter >= len(push_times):
476 push_counter = 0
477 no_text_found = 0
478 truth_table['test_for_warmup'] = False
479 truth_table['first_ocr'] = True
480 write('\nWarmup should be over in less then %s seconds!' % push_times[-1], add_time=False, push=push_urgency + 2, push_now=True)
481 break
482
483 if no_text_found >= cfg['warmup_no_text_limit']:
484 push_counter = 0
485 no_text_found = 0
486 truth_table['test_for_warmup'] = False
487 truth_table['first_ocr'] = True
488 write('\nDid not find any warmup text.', add_time=False, push=push_urgency + 2, push_now=True)
489 break
490
491exit('ENDED BY USER')