forked from belguawhale/Discord-Werewolf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbot.py
6330 lines (6083 loc) · 373 KB
/
bot.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
import discord
import asyncio
import aiohttp
import os
import random
import traceback
import sys
from datetime import datetime, timedelta
from io import BytesIO, StringIO
from config import *
from settings import *
import json
import urllib.request
from collections import OrderedDict
from itertools import chain
################## START INIT #####################
client = discord.Client()
# [playing?, {players dict}, day?, [night start, day start], [night elapsed, day elapsed], first join, gamemode, {original roles amount}]
session = [False, OrderedDict(), False, [0, 0], [timedelta(0), timedelta(0)], 0, '', {}]
PLAYERS_ROLE = None
ADMINS_ROLE = None
WEREWOLF_NOTIFY_ROLE = None
first_notify = True
notify_previous = datetime.now()
ratelimit_dict = {}
pingif_dict = {}
notify_me = []
stasis = {}
commands = {}
wait_bucket = WAIT_BUCKET_INIT
wait_timer = datetime.now()
day_warning = DEFAULT_DAY_WARNING
day_timeout = DEFAULT_DAY_TIMEOUT
night_warning = DEFAULT_NIGHT_WARNING
night_timeout = DEFAULT_NIGHT_TIMEOUT
MAX_MESSAGE_LEN = 2000
faftergame = None
starttime = None
with open(NOTIFY_FILE, 'a+') as notify_file:
notify_file.seek(0)
notify_me = notify_file.read().split(',')
if os.path.isfile(STASIS_FILE):
with open(STASIS_FILE, 'r') as stasis_file:
stasis = json.load(stasis_file)
else:
with open(STASIS_FILE, 'a+') as stasis_file:
stasis_file.write('{}')
random.seed(datetime.now())
def get_jsonparsed_data(url):
try:
response = urllib.request.urlopen(url)
except urllib.error.HTTPError:
return None, None # url does not exist
data = response.read().decode("utf-8")
return json.loads(data), data
def load_language(language):
file = 'lang/{}.json'.format(language)
if not os.path.isfile(file):
file = 'lang/en.json'
print("Could not find language file {}.json, fallback on en.json".format(language))
with open(file, 'r', encoding='utf-8') as f:
return json.load(f)
lang = load_language(MESSAGE_LANGUAGE)
def cmd(name, perms, description, *aliases):
def real_decorator(func):
commands[name] = [func, perms, description.format(BOT_PREFIX)]
for alias in aliases:
if alias not in commands:
commands[alias] = [func, perms, "```\nAlias for {0}{1}.```".format(BOT_PREFIX, name)]
else:
print("ERROR: Cannot assign alias {0} to command {1} since it is already the name of a command!".format(alias, name))
return func
return real_decorator
################### END INIT ######################
@client.event
async def on_ready():
global starttime
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
if starttime:
await log(1, 'on_ready triggered again!')
if PLAYING_MESSAGE:
await client.change_presence(status=discord.Status.online, game=discord.Game(name=PLAYING_MESSAGE))
return
await log(1, 'on_ready triggered!')
# [playing : True | False, players : {player id : [alive, role, action, template, other]}, day?, [datetime night, datetime day], [elapsed night, elapsed day], first join time, gamemode]
for role in client.get_server(WEREWOLF_SERVER).role_hierarchy:
if role.name == PLAYERS_ROLE_NAME:
global PLAYERS_ROLE
PLAYERS_ROLE = role
if role.name == ADMINS_ROLE_NAME:
global ADMINS_ROLE
ADMINS_ROLE = role
if role.name == WEREWOLF_NOTIFY_ROLE_NAME:
global WEREWOLF_NOTIFY_ROLE
WEREWOLF_NOTIFY_ROLE = role
if PLAYERS_ROLE:
await log(0, "Players role id: " + PLAYERS_ROLE.id)
else:
await log(3, "Could not find players role " + PLAYERS_ROLE_NAME)
if ADMINS_ROLE:
await log(0, "Admins role id: " + ADMINS_ROLE.id)
else:
await log(3, "Could not find admins role " + ADMINS_ROLE_NAME)
if WEREWOLF_NOTIFY_ROLE:
await log(0, "Werewolf Notify role id: " + WEREWOLF_NOTIFY_ROLE.id)
else:
await log(2, "Could not find Werewolf Notify role " + WEREWOLF_NOTIFY_ROLE_NAME)
if PLAYING_MESSAGE:
await client.change_presence(status=discord.Status.online, game=discord.Game(name=PLAYING_MESSAGE))
sync_players = False
sync_lobby = False
for member in client.get_server(WEREWOLF_SERVER).members:
if PLAYERS_ROLE in member.roles:
if not sync_players:
await send_lobby("{}, the bot has restarted, so the game has been cancelled. Type `{}join` to start a new game.".format(PLAYERS_ROLE.mention, BOT_PREFIX))
sync_players = True
await client.remove_roles(member, PLAYERS_ROLE)
perms = client.get_channel(GAME_CHANNEL).overwrites_for(client.get_server(WEREWOLF_SERVER).default_role)
if not perms.send_messages:
perms.send_messages = True
await client.edit_channel_permissions(client.get_channel(GAME_CHANNEL), client.get_server(WEREWOLF_SERVER).default_role, perms)
sync_lobby = True
if sync_players or sync_lobby:
await log(2, "SYNCED UPON BOT RESTART")
starttime = datetime.now()
@client.event
async def on_resume():
print("RESUMED")
await log(1, "on_resume triggered!")
@client.event
async def on_message(message):
if not starttime:
return
if message.author.id in [client.user.id] + IGNORE_LIST or not client.get_server(WEREWOLF_SERVER).get_member(message.author.id):
if not (message.author.id in ADMINS or message.author.id == OWNER_ID):
return
if await rate_limit(message):
return
if message.channel.is_private:
await log(0, 'pm from ' + message.author.name + ' (' + message.author.id + '): ' + message.content)
if session[0] and message.author.id in session[1]:
if session[1][message.author.id][1] in WOLFCHAT_ROLES and session[1][message.author.id][0]:
if not message.content.strip().startswith(BOT_PREFIX):
await wolfchat(message)
if message.content.strip().startswith(BOT_PREFIX):
# command
command = message.content.strip()[len(BOT_PREFIX):].lower().split(' ')[0]
parameters = ' '.join(message.content.strip().lower().split(' ')[1:])
if has_privileges(1, message) or message.channel.id == GAME_CHANNEL or message.channel.is_private:
await parse_command(command, message, parameters)
elif message.channel.is_private:
command = message.content.strip().lower().split(' ')[0]
parameters = ' '.join(message.content.strip().lower().split(' ')[1:])
await parse_command(command, message, parameters)
@client.event
async def on_member_remove(member):
member_id = member.id
member_name = member.name
if member_id in session[1]:
leave_msg = ""
if session[0] and session[1][member_id][0]:
await player_deaths({member_id: ('fleave', "bot")})
if session[6] == 'noreveal':
leave_msg += "**" + member_name + "** left the server. Farewell.\n"
else:
leave_msg += "**" + member_name + "** left the server. Farewell **" + get_role(member_id, 'death') + "**.\n"
if member_id in stasis:
stasis[member_id] += QUIT_GAME_STASIS
else:
stasis[member_id] = QUIT_GAME_STASIS
await send_lobby(leave_msg)
await log(2, "{} ({}) was FLEAVED for leaving the server IN GAME".format(member_name, member_id))
if win_condition() == None:
await check_traitor()
elif not session[0]:
await player_deaths({member_id: ('fleave', "bot")})
leave_msg += "**" + member_name + "** left the server. Farewell.\nNew player count: **{}**".format(len(session[1]))
await send_lobby(leave_msg)
await log(2, "{} ({}) was FLEAVED for leaving the server OUT OF GAME".format(member_name, member_id))
if len(session[1]) == 0:
await client.change_presence(game=client.get_server(WEREWOLF_SERVER).me.game, status=discord.Status.online)
############# COMMANDS #############
@cmd('shutdown', [2, 2], "```\n{0}shutdown takes no arguments\n\nShuts down the bot. Owner-only.```")
async def cmd_shutdown(message, parameters):
if parameters.startswith("-fstop"):
await cmd_fstop(message, "-force")
elif parameters.startswith("-stop"):
await cmd_fstop(message, parameters[len("-stop"):])
elif parameters.startswith("-fleave"):
await cmd_fleave(message, 'all')
await reply(message, "Shutting down...")
await client.logout()
@cmd('ping', [0, 0], "```\n{0}ping takes no arguments\n\nTests the bot\'s responsiveness.```")
async def cmd_ping(message, parameters):
msg = random.choice(lang['ping']).format(
bot_nick=client.user.display_name, author=message.author.name, p=BOT_PREFIX)
await reply(message, msg)
@cmd('eval', [2, 2], "```\n{0}eval <evaluation string>\n\nEvaluates <evaluation string> using Python\'s eval() function and returns a result. Owner-only.```")
async def cmd_eval(message, parameters):
output = None
parameters = ' '.join(message.content.split(' ')[1:])
if parameters == '':
await reply(message, commands['eval'][2].format(BOT_PREFIX))
return
try:
output = eval(parameters)
except:
await reply(message, '```\n' + str(traceback.format_exc()) + '\n```')
traceback.print_exc()
return
if asyncio.iscoroutine(output):
output = await output
await reply(message, '```py\n' + str(output) + '\n```', cleanmessage=False)
@cmd('exec', [2, 2], "```\n{0}exec <exec string>\n\nExecutes <exec string> using Python\'s exec() function. Owner-only.```")
async def cmd_exec(message, parameters):
parameters = ' '.join(message.content.split(' ')[1:])
if parameters == '':
await reply(message, commands['exec'][2].format(BOT_PREFIX))
return
old_stdout = sys.stdout
redirected_output = sys.stdout = StringIO()
try:
exec(parameters)
except Exception:
await reply(message, '```py\n{}\n```'.format(traceback.format_exc()))
return
finally:
sys.stdout = old_stdout
output = str(redirected_output.getvalue())
if output == '':
output = ":thumbsup:"
await client.send_message(message.channel, output)
@cmd('async', [2, 2], "```\n{0}async <code>\n\nExecutes <code> as a coroutine.```")
async def cmd_async(message, parameters, recursion=0):
if parameters == '':
await reply(message, commands['async'][2].format(BOT_PREFIX))
return
env = {'message' : message,
'parameters' : parameters,
'recursion' : recursion,
'client' : client,
'channel' : message.channel,
'author' : message.author,
'server' : message.server}
env.update(globals())
old_stdout = sys.stdout
redirected_output = sys.stdout = StringIO()
result = None
exec_string = "async def _temp_exec():\n"
exec_string += '\n'.join(' ' * 4 + line for line in parameters.split('\n'))
try:
exec(exec_string, env)
except Exception:
traceback.print_exc()
result = traceback.format_exc()
else:
_temp_exec = env['_temp_exec']
try:
returnval = await _temp_exec()
value = redirected_output.getvalue()
if returnval == None:
result = value
else:
result = value + '\n' + str(returnval)
except Exception:
traceback.print_exc()
result = traceback.format_exc()
finally:
sys.stdout = old_stdout
await client.send_message(message.channel, "```py\n{}\n```".format(result))
@cmd('help', [0, 0], "```\n{0}help <command>\n\nReturns hopefully helpful information on <command>. Try {0}list for a listing of commands.```")
async def cmd_help(message, parameters):
if parameters == '':
parameters = 'help'
if parameters in commands:
await reply(message, commands[parameters][2].format(BOT_PREFIX))
else:
await reply(message, 'No help found for command ' + parameters)
@cmd('list', [0, 0], "```\n{0}list takes no arguments\n\nDisplays a listing of commands. Try {0}help <command> for help regarding a specific command.```")
async def cmd_list(message, parameters):
cmdlist = []
for key in commands:
if message.channel.is_private:
if has_privileges(commands[key][1][1], message):
cmdlist.append(key)
else:
if has_privileges(commands[key][1][0], message):
cmdlist.append(key)
await reply(message, "Available commands: {}".format(", ".join(sorted(cmdlist))))
@cmd('join', [0, 1], "```\n{0}join [<gamemode>]\n\nJoins the game if it has not started yet. Votes for [<gamemode>] if it is given.```", 'j')
async def cmd_join(message, parameters):
global wait_timer # ugh globals
global wait_bucket
if session[0]:
return
if message.author.id in stasis and stasis[message.author.id] > 0:
await reply(message, "You are in stasis for **{}** game{}. Please do not break rules, idle out or use `!leave` during a game.".format(
stasis[message.author.id], '' if stasis[message.author.id] == 1 else 's'))
return
if len(session[1]) >= MAX_PLAYERS:
await reply(message, random.choice(lang['maxplayers']).format(MAX_PLAYERS))
return
if message.author.id in session[1]:
await reply(message, random.choice(lang['alreadyin']).format(message.author.name))
else:
session[1][message.author.id] = [True, '', '', [], []]
if len(session[1]) == 1:
wait_bucket = WAIT_BUCKET_INIT
wait_timer = datetime.now() + timedelta(seconds=WAIT_AFTER_JOIN)
client.loop.create_task(game_start_timeout_loop())
client.loop.create_task(wait_timer_loop())
await client.change_presence(game=client.get_server(WEREWOLF_SERVER).me.game, status=discord.Status.idle)
await send_lobby(random.choice(lang['gamestart']).format(
message.author.name, p=BOT_PREFIX))
else:
await client.send_message(message.channel, "**{}** joined the game and raised the number of players to **{}**.".format(
message.author.name, len(session[1])))
if parameters:
await cmd_vote(message, parameters)
# alive, role, action, [templates], [other]
await client.add_roles(client.get_server(WEREWOLF_SERVER).get_member(message.author.id), PLAYERS_ROLE)
wait_timer = datetime.now() + timedelta(seconds=WAIT_AFTER_JOIN)
client.loop.create_task(player_idle(message))
@cmd('leave', [0, 1], "```\n{0}leave takes no arguments\n\nLeaves the current game. If you need to leave, please do it before the game starts.```", 'q')
async def cmd_leave(message, parameters):
if session[0] and message.author.id in session[1] and session[1][message.author.id][0]:
if parameters != '-force':
msg = await client.send_message(message.channel, "Are you sure you want to quit during game? Doing "
"so will result in {} games of stasis. You may bypass "
"this confirmation by using `{}leave -force`.".format(
QUIT_GAME_STASIS, BOT_PREFIX))
def check(m):
c = m.content.lower()
return c in ['yes', 'y', 'no', 'n']
response = await client.wait_for_message(author=message.author, channel=message.channel, timeout=5, check=check)
await client.delete_message(msg)
if not response or response.content.lower() not in ['yes', 'y']:
return
if not session[1][message.author.id][0]:
# prevent race condition where user runs this command multiple times and then says "yes"
return
if session[6] == 'noreveal':
await send_lobby(random.choice(lang['leavedeathnoreveal']).format(message.author.name))
else:
await send_lobby(random.choice(lang['leavedeath']).format(
message.author.name, get_role(message.author.id, 'death')))
await player_deaths({message.author.id : ('leave', "bot")})
if message.author.id in stasis:
stasis[message.author.id] += QUIT_GAME_STASIS
else:
stasis[message.author.id] = QUIT_GAME_STASIS
if session[0] and win_condition() == None:
await check_traitor()
await log(1, "{} ({}) QUIT DURING GAME".format(message.author.display_name, message.author.id))
else:
if message.author.id in session[1]:
if session[0]:
await reply(message, "wot?")
return
await player_deaths({message.author.id : ('leave', "bot")})
await send_lobby(random.choice(lang['leavelobby']).format(message.author.name, len(session[1])))
if len(session[1]) == 0:
await client.change_presence(game=client.get_server(WEREWOLF_SERVER).me.game, status=discord.Status.online)
else:
await reply(message, random.choice(lang['notplayingleave']))
@cmd('wait', [0, 1], "```\n{0}wait takes no arguments\n\nIncreases the wait time until {0}start may be used.```", 'w')
async def cmd_wait(message, parameters):
global wait_bucket
global wait_timer
if session[0] or message.author.id not in session[1]:
return
if wait_bucket <= 0:
wait_bucket = 0
await reply(message, "That command is ratelimited.")
else:
wait_timer = max(datetime.now() + timedelta(seconds=EXTRA_WAIT), wait_timer + timedelta(seconds=EXTRA_WAIT))
wait_bucket -= 1
await send_lobby("**{}** increased the wait time by {} seconds.".format(message.author.name, EXTRA_WAIT))
@cmd('fjoin', [1, 1], "```\n{0}fjoin <mentions of users>\n\nForces each <mention> to join the game.```")
async def cmd_fjoin(message, parameters):
if session[0]:
return
if parameters == '':
await reply(message, commands['fjoin'][2].format(BOT_PREFIX))
return
raw_members = parameters.split(' ')
join_list = []
for member in raw_members:
if member.strip('<!@>').isdigit():
join_list.append(member.strip('<!@>'))
elif '-' in member:
left = member.split('-')[0]
right = member.split('-')[1]
if left.isdigit() and right.isdigit():
join_list += list(map(str, range(int(left), int(right) + 1)))
if join_list == []:
await reply(message, "ERROR: no valid mentions found")
return
join_msg = ""
for member in sort_players(join_list):
session[1][member] = [True, '', '', [], []]
join_msg += "**" + get_name(member) + "** was forced to join the game.\n"
if client.get_server(WEREWOLF_SERVER).get_member(member):
await client.add_roles(client.get_server(WEREWOLF_SERVER).get_member(member), PLAYERS_ROLE)
join_msg += "New player count: **{}**".format(len(session[1]))
if len(session[1]) > 0:
await client.change_presence(game=client.get_server(WEREWOLF_SERVER).me.game, status=discord.Status.idle)
await client.send_message(message.channel, join_msg)
await log(2, "{0} ({1}) used FJOIN {2}".format(message.author.name, message.author.id, parameters))
@cmd('fleave', [1, 1], "```\n{0}fleave <mentions of users | all>\n\nForces each <mention> to leave the game. If the parameter is all, removes all players from the game.```")
async def cmd_fleave(message, parameters):
if parameters == '':
await reply(message, commands['fleave'][2].format(BOT_PREFIX))
return
raw_members = parameters.split(' ')
leave_list = []
if parameters == 'all':
leave_list = list(session[1])
else:
for member in raw_members:
if member.strip('<!@>').isdigit():
leave_list.append(member.strip('<!@>'))
elif '-' in member:
left = member.split('-')[0]
right = member.split('-')[1]
if left.isdigit() and right.isdigit():
leave_list += list(map(str, range(int(left), int(right) + 1)))
if leave_list == []:
await reply(message, "ERROR: no valid mentions found")
return
leave_msg = ""
for member in sort_players(leave_list):
if member in list(session[1]):
if session[0]:
if session[6] == 'noreveal':
leave_msg += "**" + get_name(member) + "** was forcibly shoved into a fire. The air smells of freshly burnt flesh.\n"
else:
leave_msg += "**" + get_name(member) + "** was forcibly shoved into a fire. The air smells of freshly burnt **" + get_role(member, 'death') + "**.\n"
else:
leave_msg += "**" + get_name(member) + "** was forced to leave the game.\n"
leave_dict = {}
for p in [x for x in sort_players(leave_list) if x in session[1]]:
leave_dict[p] = ('fleave', "bot")
await player_deaths(leave_dict)
if not session[0]:
leave_msg += "New player count: **{}**".format(len(session[1]))
await send_lobby(leave_msg)
await log(2, "{0} ({1}) used FLEAVE {2}".format(message.author.name, message.author.id, parameters))
if session[0] and win_condition() == None:
await check_traitor()
if len(session[1]) == 0:
await client.change_presence(game=client.get_server(WEREWOLF_SERVER).me.game, status=discord.Status.online)
@cmd('refresh', [1, 1], "```\n{0}refresh [<language file>]\n\nRefreshes the current language's language file from GitHub. Admin only.```")
async def cmd_refresh(message, parameters):
global lang
if parameters == '':
parameters = MESSAGE_LANGUAGE
url = "https://raw.githubusercontent.com/belguawhale/Discord-Werewolf/master/lang/{}.json".format(parameters)
codeset = parameters
temp_lang, temp_str = get_jsonparsed_data(url)
if not temp_lang:
await reply(message, "Could not refresh language {} from Github.".format(parameters))
return
with open('lang/{}.json'.format(parameters), 'w', encoding='utf-8') as f:
f.write(temp_str)
lang = temp_lang
await reply(message, 'The messages with language code `' + codeset + '` have been refreshed from GitHub.')
@cmd('start', [0, 1], "```\n{0}start takes no arguments\n\nVotes to start the game. A game needs at least " +\
str(MIN_PLAYERS) + " players to start.```")
async def cmd_start(message, parameters):
if session[0]:
return
if message.author.id not in session[1]:
await reply(message, random.choice(lang['notplayingstart']).format(p=BOT_PREFIX))
return
if len(session[1]) < MIN_PLAYERS:
await reply(message, random.choice(lang['minplayers']).format(MIN_PLAYERS))
return
if session[1][message.author.id][1]:
return
if datetime.now() < wait_timer:
await reply(message, "Please wait at least {} more second{}.".format(
int((wait_timer - datetime.now()).total_seconds()), '' if int((wait_timer - datetime.now()).total_seconds()) == 1 else 's'))
return
session[1][message.author.id][1] = 'start'
votes = len([x for x in session[1] if session[1][x][1] == 'start'])
votes_needed = max(2, min(len(session[1]) // 4 + 1, 4))
if votes < votes_needed:
await send_lobby("**{}** has voted to start the game. **{}** more vote{} needed.".format(
message.author.display_name, votes_needed - votes, '' if (votes_needed - votes == 1) else 's'))
else:
await run_game()
if votes == 1:
await start_votes(message.author.id)
@cmd('fstart', [1, 2], "```\n{0}fstart takes no arguments\n\nForces game to start.```")
async def cmd_fstart(message, parameters):
if session[0]:
return
if len(session[1]) < MIN_PLAYERS:
await reply(message, random.choice(lang['minplayers']).format(MIN_PLAYERS))
else:
await send_lobby("**" + message.author.name + "** forced the game to start.")
await log(2, "{0} ({1}) FSTART".format(message.author.name, message.author.id))
await run_game()
@cmd('fstop', [1, 1], "```\n{0}fstop [<-force|reason>]\n\nForcibly stops the current game with an optional [<reason>]. Use {0}fstop -force if "
"bot errors.```")
async def cmd_fstop(message, parameters):
msg = "Game forcibly stopped by **" + message.author.name + "**"
if parameters == "":
msg += "."
elif parameters == "-force":
if not session[0]:
await reply(message, "There is no currently running game!")
return
msg += ". Here is some debugging info:\n```py\n{0}\n```".format(str(session))
session[0] = False
perms = client.get_channel(GAME_CHANNEL).overwrites_for(client.get_server(WEREWOLF_SERVER).default_role)
perms.send_messages = True
await client.edit_channel_permissions(client.get_channel(GAME_CHANNEL), client.get_server(WEREWOLF_SERVER).default_role, perms)
session[3] = [datetime.now(), datetime.now()]
session[4] = [timedelta(0), timedelta(0)]
session[6] = ''
session[7] = {}
await send_lobby(msg)
player_dict = {}
for player in list(session[1]):
player_dict[player] = ('fstop', "bot")
await player_deaths(player_dict)
return
else:
msg += " for reason: `" + parameters + "`."
if not session[0]:
await reply(message, "There is no currently running game!")
return
else:
await log(2, "{0} ({1}) FSTOP {2}".format(message.author.name, message.author.id, parameters))
await end_game(msg + '\n\n' + end_game_stats())
@cmd('sync', [1, 1], "```\n{0}sync takes no arguments\n\nSynchronizes all player roles and channel permissions with session.```")
async def cmd_sync(message, parameters):
for member in client.get_server(WEREWOLF_SERVER).members:
if member.id in session[1] and session[1][member.id][0]:
if not PLAYERS_ROLE in member.roles:
await client.add_roles(member, PLAYERS_ROLE)
else:
if PLAYERS_ROLE in member.roles:
await client.remove_roles(member, PLAYERS_ROLE)
perms = client.get_channel(GAME_CHANNEL).overwrites_for(client.get_server(WEREWOLF_SERVER).default_role)
if session[0]:
perms.send_messages = False
else:
perms.send_messages = True
await client.edit_channel_permissions(client.get_channel(GAME_CHANNEL), client.get_server(WEREWOLF_SERVER).default_role, perms)
await log(2, "{0} ({1}) SYNC".format(message.author.name, message.author.id))
await reply(message, "Sync successful.")
@cmd('op', [1, 1], "```\n{0}op takes no arguments\n\nOps yourself if you are an admin```")
async def cmd_op(message, parameters):
await log(2, "{0} ({1}) OP {2}".format(message.author.name, message.author.id, parameters))
if parameters == "":
await client.add_roles(client.get_server(WEREWOLF_SERVER).get_member(message.author.id), ADMINS_ROLE)
await reply(message, ":thumbsup:")
else:
member = client.get_server(WEREWOLF_SERVER).get_member(parameters.strip("<!@>"))
if member:
if member.id in ADMINS:
await client.add_roles(member, ADMINS_ROLE)
await reply(message, ":thumbsup:")
@cmd('deop', [1, 1], "```\n{0}deop takes no arguments\n\nDeops yourself so you can play with the players ;)```")
async def cmd_deop(message, parameters):
await log(2, "{0} ({1}) DEOP {2}".format(message.author.name, message.author.id, parameters))
if parameters == "":
await client.remove_roles(client.get_server(WEREWOLF_SERVER).get_member(message.author.id), ADMINS_ROLE)
await reply(message, ":thumbsup:")
else:
member = client.get_server(WEREWOLF_SERVER).get_member(parameters.strip("<!@>"))
if member:
if member.id in ADMINS:
await client.remove_roles(member, ADMINS_ROLE)
await reply(message, ":thumbsup:")
@cmd('role', [0, 0], "```\n{0}role [<role | number of players | gamemode>] [<number of players>]\n\nIf a <role> is given, "
"displays a description of <role>. If a <number of players> is given, displays the quantity of each "
"role for the specified <number of players> for the specified <gamemode>, defaulting to default. If "
"only a <gamemode> is given, displays a role guide for <gamemode>. "
"If left blank, displays a list of roles.```", 'roles')
async def cmd_role(message, parameters):
if parameters == "" and not session[0] or parameters == 'list':
roles_message = ''
roles_message += "\n```ini\n[Village Team] " + ", ".join(sort_roles(VILLAGE_ROLES_ORDERED))
roles_message += "\n[Wolf Team] " + ", ".join(sort_roles(WOLF_ROLES_ORDERED))
roles_message += "\n[Neutrals] " + ", ".join(sort_roles(NEUTRAL_ROLES_ORDERED))
roles_message += "\n[Templates] " + ", ".join(sort_roles(TEMPLATES_ORDERED)) + "```"
await reply(message, roles_message)
return
elif parameters == "" and session[0]:
msg = "**{}** players playing **{}** gamemode:```\n".format(len(session[1]),
'roles' if session[6].startswith('roles') else session[6])
if session[6] in ('random',):
msg += "!role is disabled for the {} gamemode.\n```".format(session[6])
await reply(message, msg)
return
game_roles = dict(session[7])
msg += '\n'.join(["{}: {}".format(x, game_roles[x]) for x in sort_roles(game_roles)])
msg += '```'
await reply(message, msg)
return
elif _autocomplete(parameters, roles)[1] == 1:
role = _autocomplete(parameters, roles)[0]
await reply(message, "<https://werewolf.miraheze.org/wiki/{}>\n```\nRole name: {}\nTeam: {}\nDescription: {}\n```".format(
role + "_(role)" if role == "lycan" else role.replace(' ', '_'), role, roles[role][0], roles[role][2]))
return
params = parameters.split(' ')
gamemode = 'default'
num_players = -1
choice, num = _autocomplete(params[0], gamemodes)
if num == 1:
gamemode = choice
if params[0].isdigit():
num_players = params[0]
elif len(params) == 2 and params[1].isdigit():
num_players = params[1]
if num_players == -1:
if len(params) == 2:
if params[1] == 'table':
# generate role table
WIDTH = 20
role_dict = dict()
for role in gamemodes[gamemode]['roles']:
if max(gamemodes[gamemode]['roles'][role]):
role_dict.update({role : gamemodes[gamemode]['roles'][role]})
role_guide = "Role table for gamemode **{}**:\n".format(gamemode)
role_guide += "```\n" + " " * (WIDTH + 2)
role_guide += ','.join("{}{}".format(' ' * (2 - len(str(x))), x) for x in range(gamemodes[gamemode]['min_players'], gamemodes[gamemode]['max_players'] + 1)) + '\n'
role_guide += '\n'.join(role + ' ' * (WIDTH - len(role)) + ": " + repr(
role_dict[role][gamemodes[gamemode]['min_players'] - MIN_PLAYERS:gamemodes[gamemode]['max_players']]) for role in sort_roles(role_dict))
role_guide += "\n```"
elif params[1] == 'guide':
# generate role guide
role_dict = gamemodes[gamemode]['roles']
prev_dict = dict((x, 0) for x in roles if x != 'villager')
role_guide = 'Role guide for gamemode **{}**:\n'.format(gamemode)
for i in range(gamemodes[gamemode]['max_players'] - MIN_PLAYERS + 1):
current_dict = {}
for role in sort_roles(roles):
if role == 'villager':
continue
if role in role_dict:
current_dict[role] = role_dict[role][i]
else:
current_dict[role] = 0
# compare previous and current
if current_dict == prev_dict:
# same
continue
role_guide += '**[{}]** '.format(i + MIN_PLAYERS)
for role in sort_roles(roles):
if role == 'villager':
continue
if current_dict[role] == 0 and prev_dict[role] == 0:
# role not in gamemode
continue
if current_dict[role] > prev_dict[role]:
# role increased
role_guide += role
if current_dict[role] > 1:
role_guide += " ({})".format(current_dict[role])
role_guide += ', '
elif prev_dict[role] > current_dict[role]:
role_guide += '~~{}'.format(role)
if prev_dict[role] > 1:
role_guide += " ({})".format(prev_dict[role])
role_guide += '~~, '
role_guide = role_guide.rstrip(', ') + '\n'
# makes a copy
prev_dict = dict(current_dict)
else:
role_guide = "Please choose one of the following: " + ', '.join(['guide', 'table'])
else:
role_guide = "Please choose one of the following for the third parameter: {}".format(', '.join(['guide', 'table']))
await reply(message, role_guide)
else:
num_players = int(num_players)
if num_players in range(gamemodes[gamemode]['min_players'], gamemodes[gamemode]['max_players'] + 1):
if gamemode in ('random',):
msg = "!role is disabled for the **{}** gamemode.".format(gamemode)
else:
msg = "Roles for **{}** players in gamemode **{}**:```\n".format(num_players, gamemode)
game_roles = get_roles(gamemode, num_players)
msg += '\n'.join("{}: {}".format(x, game_roles[x]) for x in sort_roles(game_roles))
msg += '```'
await reply(message, msg)
else:
await reply(message, "Please choose a number of players between " + str(gamemodes[gamemode]['min_players']) +\
" and " + str(gamemodes[gamemode]['max_players']) + ".")
async def _send_role_info(player, sendrole=True):
if session[0] and player in session[1]:
member = client.get_server(WEREWOLF_SERVER).get_member(player)
if member and session[1][player][0]:
role = get_role(player, 'role') if get_role(player, 'role') not in ['amnesiac', 'vengeful ghost', 'time lord'] else "villager"
templates = get_role(player, 'templates')
try:
if sendrole:
await client.send_message(member, "Your role is **" + role + "**. " + roles[role][2] + '\n')
msg = []
living_players = [x for x in session[1] if session[1][x][0]]
living_players_string = ['{} ({})'.format(get_name(x), x) for x in living_players]
if role in COMMANDS_FOR_ROLE['kill'] and roles[role][0] == 'wolf':
if 'angry' in session[1][player][4]:
num_kills = session[1][player][4].count('angry')
msg.append("You are **angry** tonight, and may kill {} targets by using `kill {}`.\n".format(
num_kills + 1, ' AND '.join('player' + str(x + 1) for x in range(num_kills + 1))))
if roles[role][0] == 'wolf' and role != 'cultist' and (role != 'minion' or str(session[4][1]) == "0:00:00") or role == 'minion' and sendrole:
living_players_string = []
for plr in living_players:
temprole = get_role(plr, 'role')
temptemplates = get_role(plr, 'templates')
role_string = []
if 'cursed' in temptemplates and role != 'minion':
role_string.append('cursed')
if roles[temprole][0] == 'wolf' and temprole not in ['minion', 'cultist']:
role_string.append(temprole)
living_players_string.append("{} ({}){}".format(get_name(plr), plr,
' ({})'.format(' '.join(role_string)) if role_string else ''))
if role == 'succubus':
living_players_string = []
for plr in living_players:
temprole = get_role(plr, 'role')
role_string = []
if 'entranced' in session[1][plr][4]:
role_string.append('entranced')
if temprole == 'succubus':
role_string.append(temprole)
living_players_string.append("{} ({}){}".format(get_name(plr), plr,
' ({})'.format(' '.join(role_string)) if role_string else ''))
if role == 'piper':
living_players_string = []
for plr in living_players:
temprole = get_role(plr, 'role')
role_string = []
if 'charmed' in session[1][plr][4]:
role_string.append('charmed')
if temprole == 'piper':
role_string.append(temprole)
living_players_string.append("{} ({}){}".format(get_name(plr), plr,
' ({})'.format(' '.join(role_string)) if role_string else ''))
if role == 'executioner' and sendrole:
if [x for x in session[1][player][4] if x.startswith('execute:')]:
exe_target = [x for x in session[1][player][4] if x.startswith('execute:')][0].strip('execute:')
if 'win' in session[1][player][4]:
msg.append('Your target was **{}**. This player was lynched, so you won.'.format(get_name(exe_target)))
else:
msg.append('Your target for lynch is **{}**.'.format(get_name(exe_target)))
else:
if [x for x in living_players if get_role(x, 'actualteam') == 'village']:
exe_target = random.choice([x for x in living_players if get_role(x, 'actualteam') == 'village'])
session[1][player][4].append('execute:{}'.format(exe_target))
msg.append('Your target for lynch is **{}**.'.format(get_name(exe_target)))
else:
session[1][player][1] = 'jester'
session[1][player][4].append('executioner')
await client.send_message(member, 'There are no available targets. You have now become a **jester**.\nYour role is **jester**. ' + roles['jester'][2] + '\n')
if role in ['shaman', 'wolf shaman']:
totem = ''
if session[1][player][2] in totems:
totem = session[1][player][2]
elif [x for x in session[1][player][4] if x.startswith("totem:")]:
totem = [x.split(':')[1] for x in session[1][player][4] if x.startswith("totem:")].pop()
if totem:
msg.append("You have the **{}**. {}\n".format(totem.replace('_', ' '), totems[totem]))
if role == 'clone' and sendrole:
if [x for x in session[1][player][4] if x.startswith('clone:')]:
msg.append('You are cloning **{}**.'.format(get_name([x.split(':')[1] for x in session[1][player][4] if x.startswith('clone:')].pop())))
else:
msg.append("Living players: ```basic\n" + '\n'.join(living_players_string) + '\n```')
if role in ['wolf', 'werecrow', 'doomsayer', 'wolf cub', 'werekitten', 'wolf shaman', 'wolf mystic', 'traitor', 'sorcerer', 'seer',
'oracle', 'shaman', 'harlot', 'hunter', 'augur', 'detective', 'guardian angel',
'crazed shaman', 'succubus', 'hag', 'piper', 'bodyguard', 'warlock', 'serial killer', 'hot potato']:
msg.append("Living players: ```basic\n" + '\n'.join(living_players_string) + '\n```')
#mystic stuff/wolf mystic stuff
if role == 'mystic':
wolfcount = 0
for player in session[1]:
if get_role(player, 'actualteam') == 'wolf' and session[1][player][0]:
wolfcount += 1
if "silence_totem2" in session[1][player][4]:
await client.send_message(member, "You are silenced and unable to sense anything of significance.")
else:
await client.send_message(member, "You sense that there are **{}** wolves.".format(wolfcount))
if role == 'wolf mystic':
vilcount = 0
for player in session[1]:
if ((get_role(player, 'actualteam') == 'village' and get_role(player, 'role') != 'villager') or get_role(player, 'role') in ['fool', 'monster', 'succubus', 'piper', 'demoniac', 'serial killer']) and session[1][player][0]:
vilcount += 1
if "silence_totem2" in session[1][player][4]:
await client.send_message(member, "You are silenced and unable to sense anything of significance.")
else:
await client.send_message(member, "You sense that there are **{}** villagers.".format(vilcount))
#turncoat being told when they can turn
if role == 'turncoat' and 'sided2' not in session[1][player][4]:
await client.send_message(member, "You can switch sides tonight.")
if 'gunner' in templates and (sendrole or session[1][player][4].count('bullet') > 0 or 'gunnotify' in session[1][player][4]):
msg.append("You have a gun and **{}** bullet{}. Use the command "
"`{}role gunner` for more information.".format(
session[1][player][4].count('bullet'), '' if session[1][player][4].count('bullet') == 1 else 's',
BOT_PREFIX))
if 'gunnotify' in session[1][player][4]:
session[1][player][4].remove('gunnotify')
if 'sharpshooter' in templates and (sendrole or session[1][player][4].count('bullet') > 0):
msg.append("You have a gun and **{}** bullet{}. Use the command "
"`{}role sharpshooter` for more information.".format(
session[1][player][4].count('bullet'), '' if session[1][player][4].count('bullet') == 1 else 's',
BOT_PREFIX))
if 'assassin' in templates and sendrole:
target = ""
for o in session[1][player][4]:
if o.startswith("assassinate:"):
target = o.split(":")[1]
if target:
if role == 'village drunk':
msg.append("In your drunken stupor you have selected **{0}** as your target. Use the command `{1}role assassin` for more information.".format(get_name(target), BOT_PREFIX))
else:
msg.append("Your target is **{0}**. Use the command `{1}role assassin` "
"for more information.".format(get_name(target), BOT_PREFIX))
else:
msg.append("You are an **assassin**, and wish to spread chaos. Type `target <player>` to make them your target. If you die, you take them with you, but if they die, you may choose another target.\nLiving players: ```basic\n" + '\n'.join(living_players_string) + '\n```')
if role == 'matchmaker' and sendrole:
msg.append("Living players: ```basic\n" + '\n'.join(living_players_string) + '\n```')
if role == 'minion' and (str(session[4][1]) == "0:00:00" or sendrole):
msg.append("Living players: ```basic\n" + '\n'.join(living_players_string) + '\n```')
if msg:
await client.send_message(member, '\n'.join(msg))
except discord.Forbidden:
await send_lobby(member.mention + ", you cannot play the game if you block me")
elif member and get_role(player, 'role') == 'vengeful ghost' and [x for x in session[1][player][4] if x.startswith("vengeance:")]:
try:
against = 'wolf'
if [x for x in session[1][player][4] if x.startswith("vengeance:")]:
against = [x.split(':')[1] for x in session[1][player][4] if x.startswith('vengeance:')].pop()
await client.send_message(member, "You are a **vengeful ghost**, sworn to take revenge on the {0} that you believe killed you. You must kill one of them with `kill <player>` tonight. If you do not, one of them will be selected at random.".format('wolves' if against == 'wolf' else 'villagers'))
living_players = [x for x in session[1] if session[1][x][0] if roles[get_role(x, "role")][0] == against]
living_players_string = ['{} ({})'.format(get_name(x), x) for x in living_players]
await client.send_message(member, "Living players: ```basic\n" + '\n'.join(living_players_string if living_players_string else ' ') + '\n```')
except discord.Forbidden:
pass
@cmd('myrole', [0, 0], "```\n{0}myrole takes no arguments\n\nTells you your role in pm.```")
async def cmd_myrole(message, parameters):
await _send_role_info(message.author.id)
@cmd('stats', [0, 0], "```\n{0}stats takes no arguments\n\nLists current players in the lobby during the join phase, and lists game information in-game.```")
async def cmd_stats(message, parameters):
#TODO: rewrite
if session[0]:
reply_msg = "It is now **" + ("day" if session[2] else "night") + "time**. Using the **{}** gamemode.".format(
'roles' if session[6].startswith('roles') else session[6])
reply_msg += "\n**" + str(len(session[1])) + "** players playing: **" + str(len([x for x in session[1] if session[1][x][0]])) + "** alive, "
reply_msg += "**" + str(len([x for x in session[1] if not session[1][x][0]])) + "** dead\n"
reply_msg += "```basic\nLiving players:\n" + "\n".join(get_name(x) + ' (' + x + ')' for x in session[1] if session[1][x][0]) + '\n'
reply_msg += "Dead players:\n" + "\n".join(get_name(x) + ' (' + x + ')' for x in session[1] if not session[1][x][0]) + '\n'
if session[6] in ('random',):
reply_msg += '\n!stats is disabled for the {} gamemode.```'.format(session[6])
await reply(message, reply_msg)
return
orig_roles = dict(session[7])
# make a copy
role_dict = {}
traitorvill = 0
traitor_turned = False
for other in [session[1][x][4] for x in session[1]]:
if 'traitor' in other:
traitor_turned = True
break
for role in roles: # Fixes !stats crashing with !frole of roles not in game
role_dict[role] = [0, 0]
# [min, max] for traitor and similar roles
for player in session[1]:
# Get maximum numbers for all roles
role_dict[get_role(player, 'role') if not [x for x in session[1][player][4] if x.startswith('turned:')] else [x for x in session[1][player][4] if x.startswith('turned:')].pop().split(':')[1]][0] += 1
role_dict[get_role(player, 'role') if not [x for x in session[1][player][4] if x.startswith('turned:')] else [x for x in session[1][player][4] if x.startswith('turned:')].pop().split(':')[1]][1] += 1
if get_role(player, 'role') in ['villager', 'traitor'] or 'turned:villager' in session[1][player][4]:
traitorvill += 1
#reply_msg += "Total roles: " + ", ".join(sorted([x + ": " + str(roles[x][3][len(session[1]) - MIN_PLAYERS]) for x in roles if roles[x][3][len(session[1]) - MIN_PLAYERS] > 0])).rstrip(", ") + '\n'
# ^ saved this beast for posterity
reply_msg += "Total roles: "
total_roles = dict(orig_roles)
reply_msg += ', '.join("{}: {}".format(x, total_roles[x]) for x in sort_roles(total_roles))
if session[6] == 'noreveal':
reply_msg += "```"
await reply(message, reply_msg)
return
for role in list(role_dict):
# list is used to make a copy
if role in TEMPLATES_ORDERED:
del role_dict[role]
if traitor_turned:
role_dict['wolf'][0] += role_dict['traitor'][0]
role_dict['wolf'][1] += role_dict['traitor'][1]
role_dict['traitor'] = [0, 0]
for player in session[1]:
role = get_role(player, 'role')
# Subtract dead players
if not session[1][player][0]:
reveal = get_role(player, 'deathstats')
if role == 'traitor' and traitor_turned:
# player died as traitor but traitor turn message played, so subtract from wolves
reveal = 'wolf'
if reveal == 'villager':
traitorvill -= 1
# could be traitor or villager
if 'traitor' in role_dict:
role_dict['traitor'][0] = max(0, role_dict['traitor'][0] - 1)
if role_dict['traitor'][1] > traitorvill:
role_dict['traitor'][1] = traitorvill
role_dict['villager'][0] = max(0, role_dict['villager'][0] - 1)
if role_dict['villager'][1] > traitorvill:
role_dict['villager'][1] = traitorvill
else:
# player died is definitely that role
role_dict[reveal][0] = max(0, role_dict[reveal][0] - 1)
role_dict[reveal][1] = max(0, role_dict[reveal][1] - 1)
for clone in session[1]:
if [x for x in session[1][clone][4] if x.startswith('clone:')]:
role = get_role(clone, 'role')
if (not session[1][clone][0] and role != 'clone' and orig_roles[role] > 1 and role_dict[role] != 0) or (session[1][clone][0] and role != 'clone'):
#first part - if the clone's dead but whether or not the corpse is them or a real their role, call them alive
#and the second part is if they are alive and have cloned, call them a clone instead
role_dict['clone'][0] += 1
role_dict['clone'][1] += 1
if role != 'traitor':
role_dict[role][0] -= 1
role_dict[role][1] -= 1
# after turning, amnesiac/executioner is shown instead of current role
for player in [x for x in session[1] if session[1][x][0]]:
if "amnesiac" in session[1][player][4] or "executioner" in session[1][player][4]:
role = get_role(player, 'role')
role_dict[role][0] -= 1
role_dict[role][1] -= 1
if "amnesiac" in session[1][player][4]:
role_dict["amnesiac"][0] += 1
role_dict["amnesiac"][1] += 1
else:
role_dict["executioner"][0] += 1
role_dict["executioner"][1] += 1