forked from Nubb3r/COH_Opponent_Bot
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCOHOpponentBot_Bot.py
More file actions
1627 lines (1347 loc) · 63.4 KB
/
COHOpponentBot_Bot.py
File metadata and controls
1627 lines (1347 loc) · 63.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from json import encoder
import time
import socket
import string
import sys
import json
from typing import Match # for loading json's for emoticons
import urllib.request # more for loadings jsons from urls
import collections # for deque
from decimal import *
import operator # for sorting dictionary by value
from random import choice
import os # to allow directory exists checking etc.
import os.path
import ssl
import pymem
from pymem.process import module_from_name # required for urllib certificates
import requests
#import pymysql # for mysql
#import all secret parameters from parameters file
from COHOpponentBot_Parameters import Parameters
from COHOpponentBot_ReplayParser import COH_Replay_Parser
#import the GUI
from tkinter import *
import threading
from threading import Thread
import datetime
from enum import Enum
from queue import Queue # to talk to the threads
import logging
import re
from overlayTemplates import OverlayTemplates
import html
import ctypes
from mem_edit import Process
import mem_edit
import binascii
from functools import partial
#Here are the message lines held until sent
messageDeque = collections.deque()
toSend = False
class IRCClient(threading.Thread):
def __init__(self, output, consoleDisplayBool, parameters = None):
Thread.__init__(self)
self.output = output
self.displayConsoleOut = consoleDisplayBool
if parameters:
self.parameters = parameters
else:
self.parameters = Parameters()
self.adminUserName = self.parameters.privatedata.get('adminUserName') # This username will be able to use admin commands, exit the program and bypass some limits.
#use botusername or get default if not set
if (self.parameters.data.get('botUserName') == ""):
self.nick = self.parameters.privatedata.get('IRCnick') #This value is the username used to connect to IRC eg: "xcomreborn".
else:
self.nick = self.parameters.data.get('botUserName')
self.channel = "#" + self.parameters.data.get('channel').lower() #The channel name for your channel eg: "#xcomreborn".
#use botoauthkey or get default if not set
if (self.parameters.data.get('botOAuthKey') == ""):
self.password = self.parameters.privatedata.get('IRCpassword')
else:
self.password = self.parameters.data.get('botOAuthKey')
self.server = self.parameters.privatedata.get('IRCserver')
self.port = self.parameters.privatedata.get('IRCport')
self.relicServerProxy = self.parameters.privatedata.get('relicServerProxy')
#create IRC socket
try:
self.irc = socket.socket()
except Exception as e:
logging.error("A problem occurred trying to connect")
logging.error("In IRCClient")
logging.error(str(e))
logging.exception("Exception : ")
self.irc.close()
sys.exit(0)
#irc send message buffer
self.ircMessageBuffer = collections.deque()
self.running = True
# Start checking send buffer every 3 seconds.
self.CheckIRCSendBufferEveryThreeSeconds() # only call this once.
try:
self.irc.connect((self.server, self.port))
except Exception as e:
logging.error("A problem occurred trying to connect")
logging.error("In IRCClient")
logging.error(str(e))
logging.exception("Exception : ")
self.irc.close()
sys.exit(0)
#sends variables for connection to twitch chat
self.irc.send(('PASS ' + self.password + '\r\n').encode("utf8"))
self.irc.send(('USER ' + self.nick + '\r\n').encode("utf8"))
self.irc.send(('NICK ' + self.nick + '\r\n').encode("utf8"))
self.irc.send(('CAP REQ :twitch.tv/membership' + '\r\n').encode("utf8")) # sends a twitch specific request necessary to recieve mode messages
self.irc.send(('CAP REQ :twitch.tv/tags'+ '\r\n').encode("utf8")) # sends a twitch specific request for extra data contained in the PRIVMSG changes the way it is parsed
self.irc.send(('CAP REQ :twitch.tv/commands' + '\r\n').encode("utf8")) # supposidly adds whispers
#start sub thread that uses shared Queue to communicate
# pass it irc for messaging, channel to join and queue
self.queue = Queue()
self.channelThread = IRC_Channel(self, self.irc, self.queue, self.channel, parameters=self.parameters)
self.channelThread.start()
#
# Array to hold all the new threads only neccessary if adding more channels
#
#threads = {}
#threads[self.channel] = self.channelThread
def run(self):
self.running = True
timeoutTimer = threading.Timer(5, self.connectionTimedOut)
timeoutTimer.start()
#create readbuffer to hold strings from IRC
readbuffer = ""
self.irc.setblocking(0)
# This is the main loop
while self.running:
try:
#maintain non blocking recieve buffer from IRC
readbuffer= readbuffer+self.irc.recv(1024).decode("utf-8")
temp=str.split(readbuffer, "\n")
readbuffer=temp.pop( )
for line in temp:
self.queue.put(line)
# send copy of recieved line to channel thread
line=str.rstrip(line)
line=str.split(line)
logging.info (str(line).encode('utf8'))
if (self.displayConsoleOut):
try:
message = "".join(line) + "\n"
self.SendToOutputField(message)
except Exception as e:
logging.error("In run")
logging.error(str(e))
logging.exception("Exception : ")
if (len(line) >= 3) and ("JOIN" == line[1]) and (":"+self.nick.lower()+"!"+self.nick.lower()+"@"+self.nick.lower()+".tmi.twitch.tv" == line[0]):
#cancel auto closing the thread
timeoutTimer.cancel()
try:
message = "Joined "+self.channel+" successfully.\n"
self.SendToOutputField(message)
message = "You can type 'test' in the " +self.channel[1:]+ " channel to say hello!\n"
self.SendToOutputField(message)
except Exception as e:
logging.error(str(e))
logging.exception("Exception : ")
if(line[0]=="PING"):
self.irc.send(("PONG %s\r\n" % line[0]).encode("utf8"))
except Exception as e:
pass
def connectionTimedOut(self):
try:
message = "Connection to "+self.channel+" timed out, was the channel spelt correctly and is port 6667 open?\n"
self.SendToOutputField(message)
except Exception as e:
logging.error(str(e))
logging.exception("Exception : ")
self.close()
def close(self):
self.queue.put("EXITTHREAD")
logging.info("in close in thread")
try:
# send closing message immediately
self.irc.send(("PRIVMSG " + self.channel + " :" + str("closing opponent bot") + "\r\n").encode('utf8'))
while self.channelThread.is_alive():
pass
self.running = False
except Exception as e:
logging.error("In close")
logging.error(str(e))
logging.exception("Exception : ")
def AssurePathExists(self, path):
dir = os.path.dirname(path)
if not os.path.exists(dir):
os.makedirs(dir)
def CheckIRCSendBufferEveryThreeSeconds(self):
if (self.running == True):
threading.Timer(3.0, self.CheckIRCSendBufferEveryThreeSeconds).start()
self.IRCSendCalledEveryThreeSeconds()
# above is the send to IRC timer loop that runs every three seconds
def SendPrivateMessageToIRC(self, message):
self.SendToOutputField(message) # output message to text window
message = ("PRIVMSG " + str(self.channel) + " :" + str(message) + "\r\n")
self.ircMessageBuffer.append(message) # removed this to stop message being sent to IRC
def SendWhisperToIRC(self, message, whisperTo):
try:
#whisper is currently disabled by twitch
self.ircMessageBuffer.append("PRIVMSG " + str(self.channel) + " :/w " + str(whisperTo) + " " + str(message) + "\r\n")
except Exception as e:
logging.error("Error in SendWhisperToIRC")
logging.error(str(e))
logging.exception("Exception : ")
def SendMessageToOpponentBotChannelIRC(self, message):
try:
self.ircMessageBuffer.append(("PRIVMSG " + str("#" + self.nick).lower() + " :" + str(message) + "\r\n"))
except Exception as e:
logging.error("Error in SendMessageToOpponentBotChannelIRC")
logging.error(str(e))
logging.exception("Exception : ")
def SendToOutputField(self, message):
try:
#First strip characters outside of range that cannot be handled by tkinter output field
char_list = ''
for x in range(len(message)):
if ord(message[x]) in range(65536):
char_list += message[x]
message = char_list
except Exception as e:
logging.error(str(e))
logging.exception("Exception : ")
try:
self.output.insert(END, message + "\n")
except Exception as e:
logging.error(str(e))
logging.exception("Exception : ")
def IRCSendCalledEveryThreeSeconds(self):
#print("called")
if (self.ircMessageBuffer):
try:
#print("Buffered")
stringToSend = str(self.ircMessageBuffer.popleft())
print("string to send : " + stringToSend)
self.irc.send((stringToSend).encode('utf8'))
except Exception as e:
logging.error("IRC send error:")
logging.error("In IRCSendCalledEveryThreeSeconds")
logging.error(str(e))
logging.exception("Exception : ")
#above is called by the timer every three seconds and checks for items in buffer to be sent, if there is one it'll send it
class IRC_Channel(threading.Thread):
def __init__(self, ircClient : IRCClient, irc, queue, channel, parameters = None):
Thread.__init__(self)
self.ircClient = ircClient
self.running = True
self.irc = irc
self.queue = queue
self.channel = channel
if parameters:
self.parameters = parameters
else:
self.parameters = Parameters()
self.gameData = GameData(self.ircClient, parameters=self.parameters)
def run(self):
self.irc.send(('JOIN ' + self.channel + '\r\n').encode("utf8"))
while self.running:
line = self.queue.get()
line=str.rstrip(line)
line=str.split(line)
if (line[0] == "EXITTHREAD"):
self.close()
if (line[0] == "OPPONENT"):
self.CheckForUserCommand("self","opp")
if (line[0] == "TEST"):
self.testOutput()
if (line[0] == "IWON"):
self.ircClient.SendPrivateMessageToIRC("!i won")
if (line[0] == "ILOST"):
self.ircClient.SendPrivateMessageToIRC("!i lost")
if (line[0] == "CLEAROVERLAY"):
GameData.clearOverlayHTML()
if (len(line) >= 4) and ("PRIVMSG" == line[2]) and not ("jtv" in line[0]):
#call function to handle user message
self.UserMessage(line)
def UserMessage(self, line):
# Dissect out the useful parts of the raw data line into username and message and remove certain characters
msgFirst = line[1]
msgUserName = msgFirst[1:]
msgUserName = msgUserName.split("!")[0]
#msgType = line [1];
#msgChannel = line [3]
msgMessage = " ".join(line [4:])
msgMessage = msgMessage[1:]
messageString = str(msgUserName) + " : " + str(msgMessage)
logging.info (str(messageString).encode('utf8'))
#Check for UserCommands
self.CheckForUserCommand(msgUserName, msgMessage)
if (msgMessage == "exit") and (msgUserName == self.ircClient.adminUserName):
self.ircClient.SendPrivateMessageToIRC("Exiting")
self.close()
def CheckForUserCommand(self, userName, message):
logging.info("Checking For User Comamnd")
try:
if (bool(re.match(r"^(!)?opponent(\?)?$", message.lower())) or bool(re.match(r"^(!)?place your bets$" , message.lower())) or bool(re.match(r"^(!)?opp(\?)?$", message.lower()))):
self.gameData = GameData(ircClient= self.ircClient, parameters=self.parameters)
if self.gameData.getDataFromGame():
self.gameData.outputOpponentData()
if (message.lower() == "test") and ((str(userName).lower() == str(self.parameters.privatedata.get('adminUserName')).lower()) or (str(userName) == str(self.parameters.data.get('channel')).lower())):
self.ircClient.SendPrivateMessageToIRC("I'm here! Pls give me mod to prevent twitch from autobanning me for spam if I have to send a few messages quickly.")
#self.ircClient.SendWhisperToIRC("Whisper Test", "xcoinbetbot")
self.ircClient.output.insert(END, "Oh hi again, I heard you in the " +self.channel[1:] + " channel.\n")
if (bool(re.match("^(!)?gameinfo(\?)?$", message.lower()))):
self.gameInfo()
if (bool(re.match("^(!)?story(\?)?$", message.lower()))):
self.story()
if (bool(re.match("^(!)?testoutput(\?)?$", message.lower()))):
self.ircClient.SendMessageToOpponentBotChannelIRC("!start,Test Message.")
except Exception as e:
logging.error("Problem in CheckForUserCommand")
logging.error(str(e))
logging.exception("Exception : ")
def gameInfo(self):
self.gameData = GameData(self.ircClient, parameters=self.parameters)
if self.gameData.getDataFromGame():
self.ircClient.SendPrivateMessageToIRC("Map : {}, Mod : {}, Start : {}, High Resources : {}, Automatch : {}, Slots : {}, Players : {}.".format(self.gameData.mapFullName,self.gameData.modName,self.gameData.randomStart,self.gameData.highResources, self.gameData.automatch, self.gameData.slots, self.gameData.numberOfPlayers))
def story(self):
self.gameData = GameData(self.ircClient, parameters=self.parameters)
if self.gameData.getDataFromGame():
self.ircClient.SendPrivateMessageToIRC("{}.".format(self.gameData.mapDescription))
def testOutput(self):
if not self.gameData:
self.gameData = GameData(self.ircClient)
self.gameData.testOutput()
def close(self):
self.running = False
logging.info("Closing Channel " + str(self.channel) + " thread.")
class StatsRequest:
def __init__(self, parameters = None):
if parameters:
self.parameters = parameters
else:
self.parameters = Parameters()
def returnStats(self, statnumber):
try:
logging.info ("got statnumber : " + str(statnumber))
#check stat number is 17 digit int
stringLength = len(statnumber)
assert(stringLength == 17)
assert(int(statnumber))
if (not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None)):
ssl._create_default_https_context = ssl._create_unverified_context
response = urllib.request.urlopen(self.parameters.privatedata['relicServerProxy']+str(statnumber)).read()
statdata = json.loads(response.decode('utf-8'))
if (statdata['result']['message'] == "SUCCESS"):
logging.info ("statdata load succeeded")
playerStats = PlayerStat(statdata, statnumber)
return playerStats
except Exception as e:
logging.error("Problem in returnStats")
logging.error(str(e))
logging.exception("Exception : ")
class MemoryMonitor(threading.Thread):
def __init__(self, pollInterval = 30, ircClient : IRCClient = None , parameters = None):
Thread.__init__(self)
try:
logging.info("Memory Monitor Started!")
self.running = True
if parameters:
self.parameters = parameters
else:
self.parameters = Parameters()
self.pm = None
self.baseAddress = None
self.gameInProgress = None
self.ircClient = ircClient
self.pollInterval = int(pollInterval)
self.event = threading.Event()
self.gameData = None
except Exception as e:
logging.error("In FileMonitor __init__")
logging.error(str(e))
logging.exception("Exception : ")
def run(self):
try:
while self.running:
self.getGameData()
if self.gameInProgress:
if self.gameData.gameInProgress != self.gameInProgress:
#coh was running and now its not (game over)
self.gameInProgress = self.gameData.gameInProgress
self.GameOver()
else:
if self.gameData.gameInProgress != self.gameInProgress:
#coh wasn't running and now it is (game started)
self.gameInProgress = self.gameData.gameInProgress
self.GameStarted()
self.event.wait(self.pollInterval)
#self.join()
except Exception as e:
logging.error("In FileMonitor run")
logging.error(str(e))
logging.exception("Exception : ")
def getGameData(self):
try:
self.gameData = GameData(ircClient=self.ircClient, parameters=self.parameters)
self.gameData.getDataFromGame()
except Exception as e:
logging.error("In getGameData")
logging.info(str(e))
logging.exception("Exception : ")
def GameStarted(self):
try:
self.gameData.outputOpponentData()
self.PostSteamNumber()
self.PostData()
self.StartBets()
except Exception as e:
logging.info("Problem in GameStarted")
logging.error(str(e))
logging.exception("Exception : ")
def PostSteamNumber(self):
try:
message = "!setsteam,{},{}".format(str(self.parameters.data['channel']), str(self.parameters.data['steamNumber']))
self.ircClient.SendMessageToOpponentBotChannelIRC(message)
except Exception as e:
logging.error("Problem in PostSteamNumber")
logging.exception("Exception : ")
logging.error(str(e))
def PostData(self):
try:
message = self.gameData.gameDescriptionString
self.ircClient.SendMessageToOpponentBotChannelIRC(message)
except Exception as e:
logging.error("Problem in PostData")
logging.error(str(e))
logging.exception("Exception : ")
def GameOver(self):
try:
if (self.parameters.data.get('clearOverlayAfterGameOver')):
self.ircClient.queue.put("CLEAROVERLAY")
except Exception as e:
logging.info("Problem in GameOver")
logging.error(str(e))
logging.exception("Exception : ")
def StartBets(self):
try:
logging.info("Size of self.gameData.playerList in StartBets {}".format(len(self.gameData.playerList)))
if (bool(self.parameters.data.get('writePlaceYourBetsInChat'))):
playerString = ""
outputList = []
if self.gameData:
if self.gameData.playerList:
if len(self.gameData.playerList) == 2: # if two player make sure the streamer is put first
for player in self.gameData.playerList:
outputList.append(player.name + " " + player.faction.name)
# player list does not have steam numbers. Need to aquire these from warning.log
playerString = "{} Vs. {}".format(outputList[1], outputList[0])
if self.gameData.playerList[0].stats:
if (str(self.parameters.data.get('steamNumber')) == str(self.gameData.playerList[0].stats.steamNumber)):
playerString = "{} Vs. {}".format(outputList[0], outputList[1])
self.ircClient.SendPrivateMessageToIRC("!startbets {}".format(playerString))
except Exception as e:
logging.error("Problem in StartBets")
logging.error(str(e))
logging.exception("Exception : ")
def close(self):
logging.info("Memory Monitor Closing!")
self.running = False
# break out of loops if waiting
if self.event:
self.event.set()
def find_between(self, s, first, last ):
try:
start = s.index( first ) + len( first )
end = s.index( last, start )
return s[start:end]
except ValueError:
return ""
class FileMonitor (threading.Thread):
def __init__(self, filePath, pollInterval = 30, ircClient = None, parameters = None):
Thread.__init__(self)
try:
logging.info("File Monitor Started!")
self.running = True
if parameters:
self.parameters = parameters
else:
self.parameters = Parameters()
self.ircClient = ircClient
self.filePointer = 0
self.pollInterval = int(pollInterval)
self.filePath = filePath
self.event = threading.Event()
f = open(self.filePath, 'r' , encoding='ISO-8859-1')
f.readlines()
self.filePointer = f.tell()
f.close()
logging.info("Initialzing with file length : " + str(self.filePointer) + "\n")
except Exception as e:
logging.error("In FileMonitor __init__")
logging.error(str(e))
logging.exception("Exception : ")
def run(self):
try:
logging.info ("Started monitoring File : " + str(self.filePath) + "\n")
while self.running:
lines = []
clearOverlay = False
f = open(self.filePath, 'r' , encoding='ISO-8859-1')
f.seek(self.filePointer)
lines = f.readlines()
self.filePointer = f.tell()
f.close()
for line in lines:
if ("Win notification" in line):
#Check if streamer won
theSteamNumber = self.find_between(line ,"/steam/" , "]")
if (str(self.parameters.data.get('steamNumber')) == str(theSteamNumber)):
logging.info("STREAMER WON\n")
if (self.parameters.data.get('writeIWonLostInChat')):
self.ircClient.queue.put("IWON")
if (self.parameters.data.get('clearOverlayAfterGameOver')):
clearOverlay = True
if ("Loss notification" in line):
#Check if streamer lost
theSteamNumber = self.find_between(line ,"/steam/" , "]")
if (str(self.parameters.data.get('steamNumber')) == str(theSteamNumber)):
logging.info("STREAMER LOST\n")
if (self.parameters.data.get('writeIWonLostInChat')):
self.ircClient.queue.put("ILOST")
if (self.parameters.data.get('clearOverlayAfterGameOver')):
clearOverlay = True
if (self.parameters.data.get('clearOverlayAfterGameOver')):
if (clearOverlay):
self.ircClient.queue.put("CLEAROVERLAY")
self.event = threading.Event()
self.event.wait(timeout = self.pollInterval)
logging.info ("File Monitoring Ended.\n")
#self.join()
except Exception as e:
logging.error("In FileMonitor run")
logging.error(str(e))
logging.exception("Exception : ")
def close(self):
logging.info("File Monitor Closing!")
self.running = False
# break out of loops if waiting
if self.event:
self.event.set()
def find_between(self, s, first, last ):
try:
start = s.index( first ) + len( first )
end = s.index( last, start )
return s[start:end]
except ValueError:
return ""
class PlayerStat:
def __init__(self, statdata, steamNumber):
# steamNumber is required in addition to statData to compare the steamNumber to the internal profiles that can contain other profile info
self.leaderboardData = {}
self.totalWins = 0
self.totalLosses = 0
self.totalWLRatio = None
self.steamNumber = steamNumber
self.profile_id = None
self.alias = None
self.country = None
self.steamString = None
self.steamProfileAddress = None
self.cohstatsLink = None
statString = "/steam/"+str(steamNumber)
if statdata:
if (statdata['result']['message'] == "SUCCESS"):
if statdata['statGroups'][0]['members'][0]['alias']:
for item in statdata['statGroups']:
for value in item['members']:
if (value.get('name') == statString):
self.profile_id = value.get('profile_id')
self.alias = value.get('alias')
self.steamString = value.get('name')
self.country = value.get('country')
if statdata.get('leaderboardStats'):
#print(json.dumps(statdata, indent=4, sort_keys= True))
# following number compare to leaderboard_id
# 0 is basic american
# 1 is basic wher
# 2 is basic commonWeath
# 3 is basic pe
# 4 is american 1v1
# 5 is wher 1v1
# 6 is commonWeath 1v1
# 7 is pe 1v1
# 8 is american 2v2
# 9 is wher 2v2
# 10 is commonweath 2v2
# 11 is pe 2v2
# 12 is american 3v3
# 13 is wher 3v3
# 14 is commonWeath 3v3
# 15 is pe 3v3
for item in statdata['leaderboardStats']:
#print(item)
if item.get('leaderboard_id') == 0:
self.leaderboardData[0] = factionResult(faction = Faction.US, matchType = MatchType.BASIC, name = "Americans", nameShort = "US" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 1:
self.leaderboardData[1] = factionResult(faction = Faction.WM, matchType = MatchType.BASIC, name = "Wehrmacht", nameShort = "WM" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 2:
self.leaderboardData[2] = factionResult(faction = Faction.CW, matchType = MatchType.BASIC, name = "Commonwealth", nameShort = "CW" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 3:
self.leaderboardData[3] = factionResult(faction = Faction.PE, matchType = MatchType.BASIC, name = "Panzer Elite", nameShort = "PE" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 4:
self.leaderboardData[4] = factionResult(faction = Faction.US, matchType = MatchType.ONES, name = "Americans", nameShort = "US" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 5:
self.leaderboardData[5] = factionResult(faction = Faction.WM, matchType = MatchType.ONES, name = "Wehrmacht", nameShort = "WM" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 6:
self.leaderboardData[6] = factionResult(faction = Faction.CW, matchType = MatchType.ONES, name = "Commonwealth", nameShort = "CW" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 7:
self.leaderboardData[7] = factionResult(faction = Faction.PE, matchType = MatchType.ONES, name = "Panzer Elite", nameShort = "PE" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 8:
self.leaderboardData[8] = factionResult(faction = Faction.US, matchType = MatchType.TWOS, name = "Americans", nameShort = "US" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 9:
self.leaderboardData[9] = factionResult(faction = Faction.WM, matchType = MatchType.TWOS, name = "Wehrmacht", nameShort = "WM" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 10:
self.leaderboardData[10] = factionResult(faction = Faction.CW, matchType = MatchType.TWOS, name = "Commonwealth", nameShort = "CW" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 11:
self.leaderboardData[11] = factionResult(faction = Faction.PE, matchType = MatchType.TWOS, name = "Panzer Elite", nameShort = "PE" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 12:
self.leaderboardData[12] = factionResult(faction = Faction.US, matchType = MatchType.THREES, name = "Americans", nameShort = "US" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 13:
self.leaderboardData[13] = factionResult(faction = Faction.WM, matchType = MatchType.THREES, name = "Wehrmacht", nameShort = "WM" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 14:
self.leaderboardData[14] = factionResult(faction = Faction.CW, matchType = MatchType.THREES, name = "Commonwealth", nameShort = "CW" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
if item.get('leaderboard_id') == 15:
self.leaderboardData[15] = factionResult(faction = Faction.PE, matchType = MatchType.THREES, name = "Panzer Elite", nameShort = "PE" , leaderboard_id = item.get('leaderboard_id'), wins = item.get('wins'), losses = item.get('losses'), streak = item.get('streak'), disputes = item.get('disputes'), drops = item.get('drops'), rank = item.get('rank'), rankLevel = item.get('ranklevel'), lastMatch = item.get('lastMatchDate'))
for value in self.leaderboardData:
try:
self.totalWins += int(self.leaderboardData[value].wins)
except Exception as e:
logging.error("problem with totalwins value : " + str(value) +" data : "+ str(self.leaderboardData[value].wins))
pass
try:
self.totalLosses += int(self.leaderboardData[value].losses)
except Exception as e:
logging.error("problem with totallosses value : " + str(value) +" data : "+ str(self.leaderboardData[value].losses))
pass
self.totalWins = str(self.totalWins)
self.totalLosses = str(self.totalLosses)
try:
if (int(self.totalLosses) > 0):
self.totalWLRatio = str(round(int(self.totalWins)/int(self.totalLosses), 2))
except Exception as e:
logging.error("In cohStat creating totalWLRatio")
logging.error(str(e))
logging.exception("Exception : ")
if self.steamString:
self.steamNumber = str(self.steamString).replace("/steam/", "")
self.steamProfileAddress = "steamcommunity.com/profiles/" + str(self.steamNumber)
self.cohstatsLink = "playercard.cohstats.com/?steamid="+ str(self.steamNumber)
def __str__(self):
output = ""
for value in self.leaderboardData:
output += str(self.leaderboardData[value])
output += "steamNumber : " + str(self.steamNumber) + "\n"
output += "profile_id : " + str(self.profile_id) + "\n"
output += "alias : " + str(self.alias) + "\n"
output += "country : " + str(self.country) + "\n"
output += "steamString : " + str(self.steamString) + "\n"
output += "steamProfileAddress : " + str(self.steamProfileAddress) + "\n"
output += "cohstatsLink : " + str(self.cohstatsLink) + "\n"
output += "Totals\n"
output += "Wins : " + str(self.totalWins) + "\n"
output += "Losses : " + str(self.totalLosses) + "\n"
output += "W/L Ratio : " + str(self.totalWLRatio) + "\n"
return output
class Faction(Enum):
US = 0
WM = 1
CW = 2
PE = 3
class MatchType(Enum):
BASIC = 0
ONES = 1
TWOS = 2
THREES = 3
class factionResult:
def __init__(self, faction = None, matchType = '-1',name = '-1', nameShort = '-1',leaderboard_id = '-1', wins = '-1', losses = '-1', streak = '-1', disputes = '-1', drops = '-1', rank = '-1', rankLevel = '-1', lastMatch = '-1'):
self.faction = faction
self.matchType = re.sub(r"^-1\b", "None", str(matchType))
self.name = name
self.nameShort = nameShort
self.id = leaderboard_id
self.wins = re.sub(r"^-1\b", "None" ,str(wins))
self.losses = re.sub(r"^-1\b", "None" ,str(losses))
self.streak = re.sub(r"^-1\b", "None" ,str(streak))
self.disputes = re.sub(r"^-1\b", "None" ,str(disputes))
self.drops = re.sub(r"^-1\b", "None" ,str(drops))
self.rank = re.sub(r"^-1\b", "None" ,str(rank))
self.rankLevel = re.sub(r"^-1\b", "None" ,str(rankLevel))
self.lastMatch = re.sub(r"^-1\b", "None" ,str(lastMatch))
self.lastTime = None
self.winLossRatio = None
try:
if self.lastMatch:
ts = int(self.lastMatch)
self.lastTime = str(datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S'))
except Exception as e:
logging.error("In factionResult Creating timestamp")
logging.error(str(e))
logging.exception("Exception : ")
try:
if (int(self.losses) != 0):
self.winLossRatio = str(round(int(self.wins)/int(self.losses), 2))
else:
if(int(self.wins) > 0):
self.winLossRatio = "Unbeaten"
except Exception as e:
logging.error("In factionResult Creating winLossRatio")
logging.error(str(e))
logging.exception("Exception : ")
def __str__(self):
output = "Faction : " + str(self.name) + "\n"
output += "Faction : " + str(self.faction) + "\n"
output += "matchType : " + str(self.matchType) + "\n"
output += "Short Name : "+ str(self.nameShort) + "\n"
output += "Wins : " + str(self.wins) + "\n"
output += "Losses : " + str(self.losses) + "\n"
output += "Streak : " + str(self.streak) + "\n"
output += "Disputes : " + str(self.disputes) + "\n"
output += "Drops : " + str(self.drops) + "\n"
output += "Rank : " + str(self.rank) + "\n"
output += "Level : " + str(self.rankLevel) + "\n"
output += "Last Time : " + str(self.lastMatch) + "\n"
return output
class Player:
def __init__(self, name = None, factionString = None, faction = None):
self.name = name
self.factionString = factionString
self.faction = faction
self.stats = None # This will be None for computers but point to a playerStat Object for players
if self.factionString == "axis":
self.faction = Faction.WM
if self.factionString == "allies":
self.faction = Faction.US
if self.factionString == "allies_commonwealth":
self.faction = Faction.CW
if self.factionString == "axis_panzer_elite":
self.faction = Faction.PE
def __str__(self):
output = "name : {}\n".format(str(self.name))
output += "factionString : {}\n".format(str(self.factionString))
output += "faction : {}\n".format(str(self.faction))
output += "stats : {}\n".format(str(self.stats))
return output
def __repr__(self):
return str(self)
class GameMonitor():
def __init__(self, ircClient = None, parameters = None):
if parameters:
self.parameters = parameters
else:
self.parameters = Parameters()
self.ircClient = ircClient
class GameData():
def __init__(self, ircClient = None, parameters = None):
if parameters:
self.parameters = parameters
else:
self.parameters = Parameters()
self.playerList = []
self.numberOfHumans = 0
self.numberOfComputers = 0
self.easyCPUCount = 0
self.normalCPUCount = 0
self.hardCPUCount = 0
self.expertCPUCount = 0
self.numberOfPlayers = 0
self.slots = 0
self.matchType = MatchType.BASIC
self.ircClient = ircClient
self.cohRunning = False
self.gameInProgress = False
self.gameStartedDate = None
self.cohMemoryAddress = None
self.ircStringOutputList = [] # This holds a list of IRC string outputs.
self.randomStart = None
self.highResources = None
self.VPCount = None
self.automatch = None
self.mapFullName = None
self.modName = None
self.mapDescription = ""
self.gameDescriptionString = ""
#self.placeBetsString = ""
self.pm = None
self.baseAddress = None
def getDataFromGame(self):
try:
if not self.getCOHMemoryAddress():
return False
mpPointerAddress = 0x00901EA8
mpOffsets=[0xC,0xC,0x18,0x10,0x24,0x18,0x264]
# not used but for reference
muniPointerAddress = 0x00901EA8
muniOffsets=[0xC,0xC,0x18,0x10,0x24,0x18,0x26C]
# not used but for reference
fuelPointerAddress = 0x00901EA8
fuelOffsets = [0xC,0xC,0x18,0x10,0x24,0x18,0x268]
cohrecReplayAddress = 0x00902030
cohrecOffsets = [0x28,0x160,0x4,0x84,0x24,0x110,0x0]
# check game is running by accessing player mp
mp = self.pm.read_float(self.GetPtrAddr(self.baseAddress + mpPointerAddress, mpOffsets))
# access replay data in game memory
replayData = self.pm.read_bytes(self.GetPtrAddr(self.baseAddress + cohrecReplayAddress, cohrecOffsets), 4000)
# if the above executes without throwing an error then game is in progress.
self.gameInProgress = True
cohreplayparser = COH_Replay_Parser(parameters=self.parameters)
cohreplayparser.data = bytearray(replayData)
cohreplayparser.processData()
#print(cohreplayparser)
self.gameStartedDate = cohreplayparser.localDate
self.randomStart = cohreplayparser.randomStart
self.highResources = cohreplayparser.highResources
self.VPCount = cohreplayparser.VPCount
if cohreplayparser.matchType.lower() == "automatch":
self.automatch = True
else:
self.automatch = False
if cohreplayparser.mapNameFull:
self.mapFullName = cohreplayparser.mapNameFull
else:
self.mapFullName = cohreplayparser.mapName
self.modName = cohreplayparser.modName
self.mapDescription = cohreplayparser.mapDescription
for item in cohreplayparser.playerList:
username = item['name']
factionString = item['faction']
player = Player(name=username,factionString=factionString)
self.playerList.append(player)
statList = self.getStatsFromGame()
#print(statList)
for player in self.playerList: