forked from Sirexer/SRB2DiscordBot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
srb2discordbot.py
1665 lines (1533 loc) · 88.3 KB
/
srb2discordbot.py
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
#Set title of a console
import os
consoletitle = "SRB2 DiscordBot Console"
os.system("title " + consoletitle)
#Import python modules
from colorama import init, Fore
from colorama import Back
from colorama import Style
from tkinter import messagebox
from sys import stderr
import transliterate
import subprocess
import datetime
import tkinter
import discord
import shutil
import psutil
import json
import time
import json
import sys
import re
#Reset color after a function print
init(autoreset=True)
#Hide extra window when the program gives errors
root = tkinter.Tk()
root.withdraw()
#Text output about script information
# print("Compliled with auto-py-to-exe 2.26.1 and pyinstaller 5.7.0")
# print("Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39)")
# print("Python modules:")
# print("Discord 1.7.3")
# print("Tkinter 0.1.0")
# print("Colorama 0.4.6")
# print("C data types 1.1.0")
# print("Transliterate 1.10.2")
# print("Regular Expression Engine 2.2.1")
# print("JavaScript Object Notation 2.0.9\n")
print(Fore.GREEN + Style.BRIGHT + "===============================================================================")
print(Fore.GREEN + Style.BRIGHT + " SRB2 Discord Bot - Link your SRB2 server to your Discord server ")
print(Fore.GREEN + Style.BRIGHT + " Originally created by Sirexer ")
print(Fore.GREEN + Style.BRIGHT + "===============================================================================")
print("SRB2DiscordBot v1.1 (30/05/2024)")
print(Fore.YELLOW + "Warning: Don't forget to add the lua script to SRB2, the bot won't work without it.")
print("Setting up...")
#Chech the folder of program, if it does not exist, create it
if os.path.exists("srb2discordbot") == False:
os.mkdir("srb2discordbot")
if os.path.exists("srb2discordbot/message-logs") == False:
os.mkdir("srb2discordbot/message-logs")
if os.path.exists("srb2discordbot/srb2-logs") == False:
os.mkdir("srb2discordbot/srb2-logs")
if os.path.exists("srb2discordbot/data") == False:
os.mkdir("srb2discordbot/data")
if os.path.exists("srb2discordbot/backups") == False:
os.mkdir("srb2discordbot/backups")
if os.path.exists("srb2discordbot/serverparameters") == False:
os.mkdir("srb2discordbot/serverparameters")
#Set the start date of the program for log
timestart = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
mlogpath = "srb2discordbot/message-logs/mlogs-"+timestart+".txt"
create_file = open(mlogpath, "w+")
create_file.write("")
create_file.close()
print("Message logs will be written to " + mlogpath)
#Check files for lua, if they are missing, then create them
#This is necessary to avoid errors
if os.path.exists("luafiles") == False:
os.mkdir("luafiles")
if os.path.exists("luafiles/client") == False:
os.mkdir("luafiles/client")
if os.path.exists("luafiles/client/DiscordBot") == False:
os.mkdir("luafiles/client/DiscordBot")
if os.path.isfile("latest-log.txt") == False:
create_file = open("latest-log.txt", "w+")
create_file.close()
for luafiles in "discordmessage.txt", "logcom.txt", "Messages.txt", "Players.txt", "Stats.txt", "console.txt":
if os.path.isfile("luafiles/client/DiscordBot/"+luafiles) == False:
create_file = open("luafiles/client/DiscordBot/"+luafiles, "w+")
create_file.write("")
create_file.close()
#Current map in SRB2, it is necessary to return the level
if os.path.isfile("srb2discordbot/data/map.sdb") == False:
create_file = open("srb2discordbot/data/map.sdb", "w+")
create_file.write("")
create_file.close()
#Embeb message of server stats to remove past stats
if os.path.isfile("srb2discordbot/data/stats.sdb") == False:
create_file = open("srb2discordbot/data/stats.sdb", "w+")
create_file.write("")
create_file.close()
#Current other cfg parameters
if os.path.isfile("srb2discordbot/data/cpcfg.sdb") == False:
create_file = open("srb2discordbot/data/cpcfg.sdb", "w+")
create_file.write("1")
create_file.close()
#inputs
inp_token = None
inp_channelpost = None
inp_roleping = None
inp_channelstatus = None
inp_channellog = None
inp_botprex = None
inp_srb2app = None
inp_address = None
inp_url = None
inp_debug = None
#Create config.json
def json_config():
reg_id = re.compile("[^0-9]")
global inp_token
global inp_channelpost
global inp_channelstatus
global inp_roleping
global inp_channellog
global inp_botprex
global inp_srb2app
global inp_address
global inp_url
global inp_debug
if not inp_token:
inp_token = input("Token of your discord bot: ")
while inp_token == "":
messagebox.showwarning("Warning: Empty value.", "Token is needed to work with Discord.")
inp_token = input("Token of your Discord bot: ")
inp_token = "\n\"token\": \""+inp_token+"\","
if not inp_channelpost:
inp_channelpost = input("Message channel ID: ")
while inp_channelpost == "":
messagebox.showwarning("Warning: Empty value.", "Need a channel ID where there will be messages from the game.")
inp_channelpost = input("Message channel ID: ")
inp_channelpost = (reg_id.sub('', inp_channelpost))
inp_channelpost = "\n\"post_id\": "+inp_channelpost+","
if not inp_roleping:
inp_roleping = input("Role ID that you want to ping when server starts (optional): ")
if inp_roleping == "":
inp_roleping = "\"None\""
inp_roleping = "\n\"role_id\": \""+inp_roleping+"\","
if not inp_channelstatus:
inp_channelstatus = input("Information channel ID: ")
while inp_channelstatus == "":
messagebox.showwarning("Warning: Empty value.", "Need a channel ID where there will be Information from the game.")
inp_channelstatus = input("Information channel ID: ")
inp_channelstatus = "\n\"status_id\": "+inp_channelstatus+","
if not inp_channellog:
inp_channellog = input("Log channel ID (optional): ")
if inp_channellog == "":
inp_channellog = "\"None\""
inp_channellog = (reg_id.sub('', inp_channellog))
inp_channellog = "\n\"log_id\": "+inp_channellog+","
if not inp_botprex:
inp_botprex = input("Prefix for the Discord bot. Default is '~srbot':")
if inp_botprex == "":
inp_botprex = "~srbot"
elif inp_botprex != "":
inp_botprex = inp_botprex.lower()
inp_botprex = inp_botprex.translate({ord(' '): ''})
inp_botprex = "\n\"botprefix\": \""+inp_botprex+"\","
if not inp_srb2app:
print(Fore.YELLOW + "Warning: If you are hosting multiple SRB2 servers on this computer, enter a new name for the SRB2 .exe file. For example \"my_server1\". (This is to avoid conflicts with other servers)")
inp_srb2app = input("The new name of the SRB2 .EXE file:")
if inp_srb2app == "":
inp_srb2app = "srb2win.exe"
elif inp_srb2app != "":
inp_srb2app = inp_srb2app.replace(".exe", '')
inp_srb2app = inp_srb2app+".exe"
inp_srb2app = inp_srb2app.lower()
inp_srb2app = "\n\"srb2exe\": \""+inp_srb2app+"\","
if not inp_address:
inp_address = input("IP address of SRB2 Server (optional):")
if inp_address == "":
inp_address = "0.0.0.0"
inp_address = "\n\"ip\": \""+inp_address+"\","
if not inp_url:
inp_url = input("Address where the level pictures are located (optional):")
if inp_url == "":
inp_url = "None.lol"
inp_url = "\n\"url\": \""+inp_url+"\","
if not inp_debug:
inp_debug = "\n\"debug\": false"
#write these settings to the new json file
create_file = open("srb2discordbot/config.json", "w+")
create_file.write("{"+inp_token+inp_channelpost+inp_roleping+inp_channelstatus+inp_channellog+inp_botprex+inp_srb2app+inp_address+inp_url+inp_debug+"\n}")
create_file.close()
#Check the config file, if it does not exist, create it
if os.path.isfile("srb2discordbot/config.json") == False:
print("Creating a new .json file...")
json_config()
print("Config srb2discordbot/config.json is created.")
#Execute srb2discordbot/config.json
try:
print("Executing config file srb2discordbot/config.json...")
file_json = open("srb2discordbot/config.json", "r")
config = json.load(file_json)
#If the file is damaged, then delete it and close the program
except json.decoder.JSONDecodeError:
file_json.close()
if os.path.isfile("srb2discordbot/backups/config.json") == True:
shutil.copy(r'srb2discordbot/backups/config.json', 'srb2discordbot/config.json')
messagebox.showerror("Error: Trouble with a config file.", "Invalid .json file configuration, it will be replaced from backup.")
else:
messagebox.showerror("Error: Trouble with a config file.", "Invalid .json file configuration, it will be deleted.")
os.remove("srb2discordbot/config.json")
exit()
#Check if there are all values in the config, if they are not, then add
json_notenough = False
try:
if config["token"]:
inp_token = str(config["token"])
except KeyError:
json_notenough = True
try:
if config["post_id"]:
inp_channelpost = str(config["post_id"])
except KeyError:
json_notenough = True
try:
if config["status_id"]:
inp_channelstatus = str(config["status_id"])
except KeyError:
json_notenough = True
try:
if config["log_id"]:
inp_channellog = str(config["log_id"])
except KeyError:
json_notenough = True
try:
if config["botprefix"]:
inp_botprex = str(config["botprefix"])
except KeyError:
json_notenough = True
try:
if config["srb2exe"]:
inp_srb2app = str(config["srb2exe"])
except KeyError:
json_notenough = True
try:
if config["ip"]:
inp_address = str(config["ip"])
except KeyError:
json_notenough = True
try:
if config["url"]:
inp_url = str(config["url"])
except KeyError:
json_notenough = True
try:
if config["debug"] != None:
inp_debug = str(config["debug"])
if inp_debug == "False":
inp_debug = "\n\"debug\": false"
if inp_debug == "True":
inp_debug = "\n\"debug\": true"
except KeyError:
json_notenough = True
if json_notenough == True:
print("Not all settings are set in the config. Recreating config...")
json_config()
file_json = open("srb2discordbot/config.json", "r")
config = json.load(file_json)
file_json.close()
shutil.copy(r'srb2discordbot/config.json', 'srb2discordbot/backups/config.json')
#Check if there is an exe file of the game and if there is a custom exe
if os.path.isfile(config["srb2exe"]) == False:
while os.path.isfile("srb2win.exe") == False:
messagebox.showwarning("Warning", "srb2win.exe was not found.")
if os.path.isfile(config["srb2exe"]) == True:
break
if config["srb2exe"] != "srb2win.exe" and os.path.isfile("srb2win.exe") == True:
shutil.copy(r'srb2win.exe', config["srb2exe"])
#Parameters for .exe srb2 file
print("Reading parameters from cfg file...")
if os.path.isfile("srb2discordbot/serverparameters/parameters.cfg") == False:
print("The parameters.cfg file was not found, creating a new one...")
print("Enter the parameters of the .exe file. For example \"-dedicated -server -room 33 -file...\"")
inp_parameters = input("Parameters: ")
create_file = open("srb2discordbot/serverparameters/parameters.cfg", "w+")
if inp_parameters != "":
create_file.write(inp_parameters)
elif inp_parameters == "":
create_file.write("-dedicated -server 1 -room 33 -gametype 0 -file SRB2DiscordBot.lua")
create_file.close()
#Program variables, they are neaded to have good synchronization and not only
isstarted = True
processsrb2 = None
statusfile = None
srbmessages = None
srbm = None
srbstats = None
msgstats = None
srblogcom = None
srblc = None
channel = None
embedstats = None
statchannel = None
stateditcount = 0
isdiscordborwork = False
botname = None
botavatar = None
log_lens = 0
twoxsdavoid = 0
discordcmdmsg = None
srblog = None
embed_id = None
guild = None
server_isplaying = False
client = None
roles = {}
gametype = None
avoidautorestart = True
restart_mv = False
mapvalues = []
tr_completecount = None
completecount = 0
currentmap = None
pastmap = None
restart_pcfg = None
currentpcfg = None
countpcfg = None
gamepaused = False
url_cfgs = {}
emotes = {}
bannedsavemaps = []
allowroles = []
allowmembers = []
#Create empty configuration file for autorestart
if os.path.isfile("srb2discordbot/autorestart.cfg") == False:
create_file = open("srb2discordbot/autorestart.cfg", "w+")
create_file.write("//Customize this configuration if you need an auto restart\n\n")
create_file.write("//mapvalue - restarts the server if game reaches desired map, use A0 instead of 100\n")
create_file.write("//Examples:\n")
create_file.write("//mapvalue = 01 - makes a autorestart on the map MAP01\n")
create_file.write("//mapvalue = 01,a0,b0 - makes a autorestart on the maps MAP01, MAPA0, MAPB0\n")
create_file.write("//mapvalue = none - does not autorestart due map, use \"none\" if you don't need it\n")
create_file.write("mapvalue = none\n\n")
create_file.write("//countvalue - restarts the server if so many maps have passed\n")
create_file.write("//Examples:\n")
create_file.write("//countvalue = 32 - if players have completed 32 levels no matter what, the server restarts\n")
create_file.write("//countvalue = none - doing nothing\n")
create_file.write("countvalue = none\n\n")
create_file.write("//parameter_cfgs - how many parameters and they will change, this will not work if the server has been rebooted and due to errors or has been closed by the user\n")
create_file.write("//Examples:\n")
create_file.write("//parameter_cfgs = 3 - creates three parameter files and will change them after autorestart\n")
create_file.write("//parameter_cfgs = 1 - doesn't create or change parameters, \"none\" can also be used\n")
create_file.write("parameter_cfgs = none\n\n")
create_file.write("//url_cfgX - changes link to images, if parameter_cfgs is greater than 3, make them larger\n")
create_file.write("//Examples:\n")
create_file.write("//url_cfg1 = srb2picturemap.org - Link will be changed for parameter number 1\n")
create_file.write("//url_cfg2 = default - uses link from config.cfg\n")
create_file.write("//url_cfg3 = none - removes the link\n")
create_file.write("url_cfg1 = default\n")
create_file.write("url_cfg2 = default\n")
create_file.write("url_cfg3 = default\n")
create_file.write("//url_cfg4 = default\n")
create_file.close()
#Read autorestart.cfg
if os.path.isfile("srb2discordbot/autorestart.cfg") == True:
file_autorestart = open("srb2discordbot/autorestart.cfg", "r")
while True:
#Current time
now = datetime.datetime.now()
line = file_autorestart.readline()
if not line:
break
if line.find('=') == -1:
continue
if line.startswith("//"):
continue
line = line.translate({ord(' '): ''})
line = line.translate({ord('\n'): ''})
line = line.translate({ord(','): ' '})
equals = line.find('=')
if len(line) == equals+1:
continue
if line[equals+1:].lower().find("none") != -1 and not line.startswith("url_cfg"):
continue
if line[:equals].lower() == "mapvalue":
restart_mv = True
mapvalues = line[equals+1:].split()
str_mapvalues = str(mapvalues)
str_mapvalues = str_mapvalues.translate({ord(' '): ''})
str_mapvalues = str_mapvalues.translate({ord('['): ''})
str_mapvalues = str_mapvalues.translate({ord(']'): ''})
str_mapvalues = str_mapvalues.translate({ord("'"): ''})
print("Autorestart will turn on when the game reaches: "+str_mapvalues.upper())
str_mapvalues = None
elif line[:equals].lower() == "countvalue":
try:
tr_completecount = int(line[equals+1:])
except ValueError:
print("[" + now.strftime("%H:%M") + "]" + Fore.RED + 'Error:' + Style.RESET_ALL + ' ValueError in countvalue (expected intriger, got a letter)')
continue
print("Autorestart when "+str(tr_completecount)+" maps are completed")
elif line[:equals].lower() == "parameter_cfgs":
try:
restart_pcfg = int(line[equals+1:])
except ValueError:
print("[" + now.strftime("%H:%M") + "]" + Fore.RED + 'Error:' + Style.RESET_ALL + ' ValueError in parameter_cfgs (expected intriger, got a letter)')
continue
if restart_pcfg <= 1:
restart_pcfg = None
continue
if os.path.isfile("srb2discordbot/serverparameters/pcfg1.cfg") == False:
print("Such parameters with a number will be reloaded after autorestart(which is specified in autorestart.cfg), the parameters.cfg file will always be used")
print("Sequence: pcfg(count).cfg before parameters.cfg")
for i in range(restart_pcfg):
if os.path.isfile("srb2discordbot/serverparameters/pcfg"+str(i+1)+".cfg") == False:
int_pcfg = input("parameters for "+"pcfg"+str(i+1)+".cfg:")
create_file = open("srb2discordbot/serverparameters/pcfg"+str(i+1)+".cfg", "w+")
create_file.write(int_pcfg)
create_file.close()
if restart_pcfg:
if restart_pcfg >= 2:
for i in range(restart_pcfg):
if line[:equals].lower() == "url_cfg"+str(i+1):
line = line.translate({ord(' '): ''})
line
line = line.translate({ord(','): ''})
if line[equals+1:].lower() == "none":
url_cfgs[str(i+1)] = "none"
print("url - removed for pcfg"+str(i+1)+".cfg")
elif line[equals+1:].lower() == "default":
url_cfgs[str(i+1)] = "default"
print("url - default for pcfg"+str(i+1)+".cfg")
elif line[equals+1:].find(".") == -1:
url_cfgs[str(i+1)] = "default"
print("Invalid link, no generic top-level domain for pcfg"+str(i+1)+".cfg")
else:
url_cfgs[str(i+1)] = line[equals+1:]
print("url - "+line[equals+1:]+" for pcfg"+str(i+1)+".cfg")
now = None
#Create empty configuration file for emotes
if os.path.isfile("srb2discordbot/emotes.cfg") == False:
create_file = open("srb2discordbot/emotes.cfg", "w+")
create_file.write("//Emotes work on player skins, emeralds, ping, admin and completed\n")
create_file.write("//Make sure it doesn't exceed 1024 characters in a single playerlist, otherwise there will be an error/n")
create_file.write("//character emote example: \"sonic = :cyclone:\"\n\n")
create_file.write("ping_red = :red_circle:\n")
create_file.write("ping_yellow = :yellow_circle:\n")
create_file.write("ping_green = :green_circle:\n")
create_file.write("ping_blue = :blue_circle:\n")
create_file.write("remote_admin = :star:\n")
create_file.write("completed = :checkered_flag:\n")
create_file.write("unknown = :grey_question:\n")
create_file.write("spectator = :eye:\n")
create_file.write("dead = :skull:\n")
create_file.close()
totalemotes = 0
#Read emotes.cfg
if os.path.isfile("srb2discordbot/emotes.cfg") == True:
file_emotes = open("srb2discordbot/emotes.cfg", "r")
while True:
line = file_emotes.readline()
if not line:
break
if line.find('=') == -1:
continue
if line.startswith("//"):
continue
line = line.translate({ord(' '): ''})
line = line.translate({ord('\n'): ''})
equals = line.find('=')
if len(line) == equals+1:
continue
emote_type = ":"+line[:equals].lower()+":"
emote_value = line[equals+1:]
emotes[emote_type] = emote_value
totalemotes = totalemotes + 1
if totalemotes > 0:
if totalemotes == 1:
print("Added "+str(totalemotes)+" emote")
else:
print("Added "+str(totalemotes)+" emotes")
totalemotes = None
#Create empty configuration for dontsavemaps
#Which means that after the restart there will be a map before it
if os.path.isfile("srb2discordbot/dontsavemap.cfg") == False:
create_file = open("srb2discordbot/dontsavemap.cfg", "w+")
create_file.write("//These maps will not be saved\n")
create_file.write("//it is useful that special stages are not included after the restart\n\n")
create_file.write("//Single Special Stages\n")
create_file.write("50,51,52,53,54,55,56,57\n")
create_file.write("//Multiplayer Special Stages\n")
create_file.write("60,61,62,63,64,65,66,67")
create_file.close()
#Read dontsavemap.cfg
if os.path.isfile("srb2discordbot/dontsavemap.cfg") == True:
file_maps = open("srb2discordbot/dontsavemap.cfg", "r")
while True:
line = file_maps.readline()
if not line:
break
if line.startswith("//"):
continue
line = line.translate({ord(','): ' '})
line = line.translate({ord('\n'): ''})
if line == "":
continue
line = line.lower()
bannedsavemaps = bannedsavemaps + line.split()
str_bsm = str(bannedsavemaps)
str_bsm = str_bsm.translate({ord(' '): ''})
str_bsm = str_bsm.translate({ord('['): ''})
str_bsm = str_bsm.translate({ord(']'): ''})
str_bsm = str_bsm.translate({ord("'"): ''})
print("list of maps that will not save: "+str_bsm.upper())
if os.path.isfile("srb2discordbot/commandperms.cfg") == False:
create_file = open("srb2discordbot/commandperms.cfg", "w+")
create_file.write("//Access to administrative command for members and roles\n\n")
create_file.write("//Roles ID\n")
create_file.write("roles = none\n")
create_file.write("//Members ID\n")
create_file.write("members = none")
create_file.close()
if os.path.isfile("srb2discordbot/commandperms.cfg") == True:
file_commandperms = open("srb2discordbot/commandperms.cfg", "r")
while True:
#Current time
now = datetime.datetime.now()
line = file_commandperms.readline()
if not line:
break
if line.find('=') == -1:
continue
if line.startswith("//"):
continue
line = line.translate({ord(' '): ''})
line = line.translate({ord('\n'): ''})
line = line.translate({ord(','): ' '})
equals = line.find('=')
if len(line) == equals+1:
continue
if line[equals+1:].lower().find("none") != -1:
continue
if line[:equals].lower() == "roles":
restart_mv = True
allowroles = line[equals+1:].split()
str_allowroles = str(allowroles)
str_allowroles = str_allowroles.translate({ord(' '): ''})
str_allowroles = str_allowroles.translate({ord('['): ''})
str_allowroles = str_allowroles.translate({ord(']'): ''})
str_allowroles = str_allowroles.translate({ord("'"): ''})
print("Allowed roles for using commands: "+str_allowroles.upper())
str_allowroles = None
if line[:equals].lower() == "members":
restart_mv = True
allowmembers = line[equals+1:].split()
str_allowmembers = str(allowmembers)
str_allowmembers = str_allowmembers.translate({ord(' '): ''})
str_allowmembers = str_allowmembers.translate({ord('['): ''})
str_allowmembers = str_allowmembers.translate({ord(']'): ''})
str_allowmembers = str_allowmembers.translate({ord("'"): ''})
print("Allowed members for using commands: "+str_allowmembers.upper())
str_allowmembers = None
#Change advenced parameters
def changeparameters():
global restart_pcfg
global currentpcfg
global countpcfg
if restart_pcfg:
#Don't change it, if advenced parameters is empty
if countpcfg == None:
countpcfg_file = open("srb2discordbot/data/cpcfg.sdb", "r")
try:
countpcfg = int(countpcfg_file.read())
except ValueError:
if config["debug"] == True:
#Current time
now = datetime.datetime.now()
print("[" + now.strftime("%H:%M") + "]" + Fore.RED + 'Error:' + Style.RESET_ALL + ' ValueError in cpcfg.sdb (expected intriger, got string)')
countpcfg = 1
countpcfg_file.close()
if countpcfg == "":
countpcfg = 1
if countpcfg > restart_pcfg:
countpcfg = 1
countpcfg_file = open("srb2discordbot/data/cpcfg.sdb", "w")
countpcfg_file.write(str(countpcfg))
countpcfg_file.close()
currentpcfg_file = open("srb2discordbot/serverparameters/pcfg"+str(countpcfg)+".cfg", "r")
currentpcfg = currentpcfg_file.read()
currentpcfg_file.close()
#Change it, if advenced parameters is not empty
elif countpcfg != None:
if countpcfg != restart_pcfg:
countpcfg = countpcfg + 1
else:
countpcfg = 1
currentpcfg_file = open("srb2discordbot/serverparameters/pcfg"+str(countpcfg)+".cfg", "r")
currentpcfg = currentpcfg_file.read()
currentpcfg_file.close()
countpcfg_file = open("srb2discordbot/data/cpcfg.sdb", "w")
countpcfg_file.write(str(countpcfg))
countpcfg_file.close()
def write_to_log(prefix, message):
mlogs_file = open(mlogpath, "a")
mlogs_file.write("[" + now.strftime("%Y-%m-%d %H:%M:%S")+"]"+prefix+": "+message+"\n")
mlogs_file.close()
#Starting SRB2 Server
def startserver():
global isstarted
global srblog
global server_isplaying
global currentpcfg
print("Starting server...")
if currentpcfg == None:
changeparameters()
isstarted = False
for proc in psutil.process_iter():
name = proc.name()
#Check if the server is running
if name == config["srb2exe"]:
#If the server is running, then wait for an answer
isstarted = True
restartsrb2 = input("Server already running, restart? (Y/n):")
if restartsrb2.lower() == "yes" or restartsrb2.lower() == "y" or restartsrb2.lower() == "of course" or restartsrb2.lower() == "yeah":
#Close the server if the answer is yes
isstarted = False
shutil.copy(r'latest-log.txt', "srb2discordbot/srb2-logs/log-" + timestart + ".txt")
os.system("taskkill /f /im " + config["srb2exe"])
log_file = open("latest-log.txt", "w")
log_file.write("")
log_file.close()
else:
server_isplaying = True
#Get Gametype
file_concole = open('luafiles/client/DiscordBot/console.txt', 'a')
file_concole.write('gametype\n')
file_concole.close
break
if isstarted == False:
#Read parameters for the server
file_parameters = open("srb2discordbot/serverparameters/parameters.cfg", "r")
parameters = file_parameters.read()
file_parameters.close()
#Return the level that was the last time
file_mapsrb2 = open("srb2discordbot/data/map.sdb", "r")
mapsrb2 = file_mapsrb2.read()
file_mapsrb2.close()
#Start the server
log_file = open("latest-log.txt", "w")
log_file.write("")
log_file.close()
if currentpcfg == None:
processsrb2 = subprocess.Popen(config["srb2exe"] + " " + mapsrb2 + " " + parameters)
else:
processsrb2 = subprocess.Popen(config["srb2exe"] + " " + mapsrb2 + " " + currentpcfg + " " + parameters)
isstarted = True
#Restarting the server if it is closed for any reason
def restartserver():
global isstarted
global srblog
global server_isplaying
global currentpcfg
global countpcfg
global avoidautorestart
server_isplaying = False
avoidautorestart = True
if srblog != None:
shutil.copy(r'latest-log.txt', "srb2discordbot/srb2-logs/" + srblog)
log_file = open("latest-log.txt", "w")
log_file.write("")
log_file.close()
print("Restarting server... ")
for proc in psutil.process_iter():
name = proc.name()
#Close the crashed server
if name == config["srb2exe"]:
os.system("taskkill /f /im " + config["srb2exe"])
break
#Read parameters for the server
file_parameters = open("srb2discordbot/serverparameters/parameters.cfg", "r")
parameters = file_parameters.read()
file_parameters.close()
#Return the level that was the last time
file_mapsrb2 = open("srb2discordbot/data/map.sdb", "r")
mapsrb2 = file_mapsrb2.read()
file_mapsrb2.close()
#restart the server
if countpcfg:
currentpcfg_file = open("srb2discordbot/serverparameters/pcfg"+str(countpcfg)+".cfg", "r")
currentpcfg = currentpcfg_file.read()
currentpcfg_file.close()
if currentpcfg == None:
processsrb2 = subprocess.Popen(config["srb2exe"] + " " + mapsrb2 + " " + parameters)
else:
processsrb2 = subprocess.Popen(config["srb2exe"] + " " + mapsrb2 + " " + currentpcfg + " " + parameters)
isstarted = True
#This function creates an embed containing information about the SRB2 server
def getsrbstats():
#Get global variables
global embedstats
global botname
global botavatar
global gametype
global restart_pcfg
global currentpcfg
global countpcfg
global url_cfgs
#Each line in the embed is 1024 characters max
#So the list of the players divided into parts
players = {1: '', 2: '', 3: '', 4: '',5: '', 6: '', 7: '', 8: ''}
try:
cline = 0 #Number of rows in one list
plist = 1 #Numbers of lists
#Open statistics of SRB2 Server
srbstats = open('luafiles/client/DiscordBot/Stats.txt', 'r')
#Get server name
reg = re.compile("[^a-zA-Z0-9 !@#$%^&*()-_=+{}[]|?<>.,`~]")
servername = srbstats.readline()
servername = (reg.sub('', servername))
#Get level name and map number
gamemap = srbstats.readline()
#Get map number only
iconmap = srbstats.readline()
#Get nextmap and map number
nextlevel = srbstats.readline()
#Get info about emeralds
emeralds = srbstats.readline()
for element in emotes:
emeralds = emeralds.replace(element, emotes[element])
#Get all skins on the server
skins = srbstats.readline()
for element in emotes:
skins = skins.replace(element, emotes[element])
#Get how much time has passed on a level
leveltime = srbstats.readline()
#Get how much time has passed since the launch of SRB2 server
servertime = srbstats.readline()
#Get count of players
countp = srbstats.readline()
#Get link of level image
linktb = "http://"+config["url"]+"/levelpictures/thumbnail/"+iconmap+".png"
if restart_pcfg:
try:
if url_cfgs[str(countpcfg)].lower() == "default":
linktb = "http://"+config["url"]+"/levelpictures/thumbnail/"+iconmap+".png"
elif url_cfgs[str(countpcfg)].lower() == "none":
linktb = "http://none.kek/levelpictures/thumbnail/"+iconmap+".png"
else:
linktb = "http://"+url_cfgs[str(countpcfg)]+"/levelpictures/thumbnail/"+iconmap+".png"
except KeyError:
pass
#Remove a line break in URL
linktb = (linktb.translate({ord('\n'): ''}))
#EMBEDS!
embedstats=discord.Embed(title=servername+" Statistics", url="https://bit.ly/3APCzSQ", description="Server information", color=0x00feff)
embedstats.set_author(name=botname, url="https://ms.srb2.org", icon_url=botavatar)
embedstats.set_thumbnail(url=linktb)
embedstats.add_field(name="Leveltime", value=leveltime, inline=True)
embedstats.add_field(name="Servertime", value=servertime, inline=True)
embedstats.add_field(name="Players", value=countp, inline=True)
embedstats.add_field(name="Map", value=gamemap, inline=True)
embedstats.add_field(name="Nextmap", value=nextlevel, inline=True)
if gametype:
embedstats.add_field(name="Gametype", value=gametype, inline=True)
embedstats.add_field(name="Emeralds", value=emeralds, inline=True)
embedstats.add_field(name="Skins", value=skins, inline=True)
#Split player list
while True:
if cline != 6 or (cline == 6 and plist == 8):
line = srbstats.readline()
for element in emotes:
line = line.replace(element, emotes[element])
players[plist] = players[plist]+line
cline = cline + 1
if not line:
break
#If there are 8 lines, create a new list and save the first list
else:
embedstats.add_field(name="Players list#"+str(plist), value=(players[plist]), inline=False)
plist = plist +1
cline = 0
#Last list
if players[plist] != '':
embedstats.add_field(name="Players list#"+str(plist), value=(players[plist]), inline=False)
embedstats.set_footer(text=servername+" "+config["ip"])
srbstats.close()
#Change a title of console
consoletitle = ("SRB2 DiscordBot Console: " + servername)
os.system("title " + consoletitle)
except:
if config["debug"] == True:
print("[" + now.strftime("%H:%M") + "]" + Fore.RED + 'Error:' + Style.RESET_ALL + ' trouble in embed')
class MyClient(discord.Client):
async def on_ready(self):
global client
try:
await client.change_presence(status=discord.Status.dnd,activity=discord.Game("Launching a bot..."))
except ConnectionResetError:
if config["debug"] == True: #Display where the error occurred if debug is enabled
print("[" + now.strftime("%H:%M") + "]" + Fore.RED + 'Error:' + Style.RESET_ALL + ' failed to update status')
#Get global variables
global srbmessages
global srbm
global channel
global srbstats
global msgstats
global statchannel
global embedstats
global stateditcount
global srblogcom
global srblc
global embed_id
global guild
global statusfile
global botname
global botavatar
global isdiscordborwork
global isstarted
global srblog
global twoxsdavoid
global log_lens
global processsrb2
global roles
global restart_mv
global mapvalues
global tr_completecount
global completecount
global bannedsavemaps
global gametype
global avoidautorestart
global server_isplaying
global currentmap
global pastmap
global gamepaused
#variables for message status
global shutdown_message
shutdown_message = "❌ `Server has shut down.`"
global startup_message
startup_message = "✅ `Server has started!`"
if inp_roleping != "None":
startup_message = f"✅ <@&{inp_roleping}> `Server has started!`"
#Print bot name to console on successful connection
print(Style.BRIGHT + Back.GREEN + 'SUCCESS:' + Style.RESET_ALL + Fore.GREEN + ' logged on as {0}!'.format(self.user))
config["botprefix"] = config["botprefix"].translate({ord(' '): ''})
print("Prefix for bot is "+config["botprefix"])
try:
guild_id = client.get_channel(config["post_id"]).guild.id
#print(guild_id)
guild = client.get_guild(guild_id)
#print(guild)
#print(",".join([str(r.id) for r in guild.roles]))
#If the channel ID is not correct, display an error window and close the program
except AttributeError:
if config["debug"] == True:
print("[" + now.strftime("%H:%M") + "]" + Fore.RED + 'Error:' + Style.RESET_ALL + ' failed to get a guild ID.')
messagebox.showerror("Error: Improper Message or Log Channel ID has been passed.", "Check the channel ID, change it in the discordbot.json file.")
exit()
#Get bot name and avatar for embed
botname = self.user
botavatar = self.user.display_avatar
twoxsdavoid = 2
startserver()
#Program loop
while True:
try:
#Current time
now = datetime.datetime.now()
#Opening the log file
file_log = open("latest-log.txt", "r")
log = [l.strip() for l in file_log.readlines()]
if log_lens != 0:
#Don't read what was readed
for line in log[log_lens:]:
if line.startswith("Entering main game loop..."):
await client.get_channel(config["post_id"]).send(startup_message)
print("[" + now.strftime("%H:%M") + "]" + Fore.YELLOW + "SRB2 Status Message: " + Style.RESET_ALL + "Server has been started!")
write_to_log("SRB2 Status Message:", "Server has been started!")
server_isplaying = True
elif line.startswith("Current gametype is "):
gametype = line[20:]
elif line.startswith("Logfile:"):
srblog = line[16:]
elif line.startswith("Map is now"):
#Write the current map to a file
if pastmap == None:
pastmap = line[12:17]
dbm = False
for element in bannedsavemaps:
if line[12:17].lower() == ("map"+element):
dbm = True
continue
if dbm == False:
if currentmap != None:
pastmap = currentmap
currentmap = line[12:17]
mapsrb2 = "+map " + line[12:17]
file_mapsrb2 = open("srb2discordbot/data/map.sdb", "w")
file_mapsrb2.write(mapsrb2)
file_mapsrb2.close()
dbm = None
if tr_completecount:
completecount = completecount + 1
if currentmap == pastmap:
avoidautorestart = True
if avoidautorestart == False:
if restart_mv == True:
for element in mapvalues:
if "map"+element.lower() == line[12:17].lower():
print("[" + now.strftime("%H:%M") + "]" + Fore.YELLOW + "SRB2 Status Message: " + Style.RESET_ALL + "Autorestart!")
await client.get_channel(config["post_id"]).send("🔄 `Autorestart!`")
await client.change_presence(status=discord.Status.dnd,activity=discord.Game("Restarting server..."))
write_to_log("SRB2 Status Message:", "Autorestart!")
avoidautorestart = True
if tr_completecount:
if completecount > tr_completecount:
completecount = 0
changeparameters()
restartserver()
#tr_completecount completecount
if tr_completecount != None:
if avoidautorestart == False:
if tr_completecount+1 == completecount:
print("[" + now.strftime("%H:%M") + "]" + Fore.YELLOW + "SRB2 Status Message: " + Style.RESET_ALL + "Autorestart!")
await client.get_channel(config["post_id"]).send("🔄 `Autorestart!`")
await client.change_presence(status=discord.Status.dnd,activity=discord.Game("Restarting server..."))
write_to_log("SRB2 Status Message:", "Autorestart!")
completecount = 0
changeparameters()
restartserver()
else:
avoidautorestart = False
if server_isplaying == True: