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