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