-
Notifications
You must be signed in to change notification settings - Fork 1
/
game_main.py
457 lines (364 loc) · 20.6 KB
/
game_main.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
##################
# Import Modules #
##################
from discord.ext import commands
from discord import utils, ChannelType
from fuzzywuzzy import fuzz
import re
###########################
# Import External Classes #
###########################
from database_connection import dbcursor
from logging_setup import logging
import errors as MonopolyRunError
##############
# Game Class #
##############
class claGame(commands.Cog):
####################
# Initialize Class #
####################
def __init__(self, bot):
self.bot = bot
self.funTeamRole = claGame.funTeamRole
# Define some lists to use later #
self.lisProperties = ['brown1', 'brown2', 'lightblue1', 'lightblue2', 'lightblue3', 'pink1', 'pink2', 'pink3', 'orange1', 'orange2', 'orange3', 'red1', 'red2', 'red3', 'yellow1', 'yellow2', 'yellow3', 'green1', 'green2', 'green3', 'darkblue1', 'darkblue2', 'black1', 'black2', 'black3', 'black4']
##############
# Converters #
##############
# Convert to lowercase #
def funToLower(strString):
strNewString = re.sub("\s+", '', strString)
strNewString = strNewString.lower()
return strNewString
#############
# Functions #
#############
# Return users team role #
def funTeamRole(ctx):
lisUserRoles = [r.name for r in ctx.author.roles]
lisUserRoles.reverse()
r = re.compile("team.*")
if not list(filter(r.match, lisUserRoles)):
raise commands.MissingRole("team?")
return utils.get(ctx.author.roles, name=next(filter(r.match, lisUserRoles)))
##################
# Command Checks #
##################
# Check if command is being used in a team channel #
def funRoleChannelCheck():
async def predicate(ctx):
# Check if command is being used in a DM #
if ctx.channel.type is ChannelType.private:
raise commands.NoPrivateMessage
# Check if guild is setup #
strGuildID = str(ctx.guild.id)
dbcursor.execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'monopolyrun' AND TABLE_NAME = ?", (f'tbl_{strGuildID}', ))
lisGuildTable = dbcursor.fetchall()
if not lisGuildTable:
raise MonopolyRunError.DatabaseTableNotFound
# Check if the user has a team role #
lismyRoles = [r.name for r in ctx.author.roles]
lismyRoles.reverse()
r = re.compile("team.*")
if not list(filter(r.match, lismyRoles)):
raise commands.MissingRole("team?")
# Check if user is using the command in their team channel #
if re.search('team', ctx.channel.name) is None:
raise commands.ChannelNotFound("team?")
x = str(ctx.channel.name)
y = str(next(filter(r.match, lismyRoles)))
if x != y:
raise MonopolyRunError.NotInTeamChannel(next(filter(r.match, lismyRoles)), ctx.channel.name)
return True
return commands.check(predicate)
#################
# Go To Command #
#################
@commands.command()
@funRoleChannelCheck()
async def goto(self, ctx, *,strProperty: funToLower):
# Declare some key variables #
strGuildID = str(ctx.guild.id)
strTeamName = str(self.funTeamRole(ctx))
# Check if property exists but try and guess #
if strProperty not in self.lisProperties:
for strValidProperty in self.lisProperties:
intPartialRatio = fuzz.partial_ratio(strValidProperty, strProperty)
intTokenSetRatio = fuzz.token_set_ratio(strValidProperty, strProperty)
if intTokenSetRatio >= 80 or intTokenSetRatio >= 80:
await ctx.send(f':tophat: Could not find property however I have guessed you want: {strValidProperty}')
strProperty = strValidProperty
break
# Check if property exists #
if strProperty not in self.lisProperties:
raise MonopolyRunError.InvalidPropertyName(strProperty)
# Get money and visited from the database #
dbcursor.execute(f"SELECT {strProperty}_visited FROM tbl_{strGuildID} WHERE id = ?", (strTeamName, ))
lisVisted = dbcursor.fetchall()
tupVisted = lisVisted[0]
strVisted = tupVisted[0]
# Check if the team has already visited that property #
if strVisted == 'Y':
raise MonopolyRunError.AlreadyVisted(strProperty)
# Get which set of Questions the guild is using #
dbcursor.execute("SELECT questions FROM tbl_guilds WHERE id = ?", (strGuildID, ))
lisQuestions = dbcursor.fetchall()
tupQuestions = lisQuestions[0]
strQuestions = tupQuestions[0]
# Get Location and Question from the Database #
dbcursor.execute(f"SELECT location, question FROM tbl_{strQuestions} WHERE id = ?", (strProperty, ))
lisLocationQuestion = dbcursor.fetchall()
for item in lisLocationQuestion:
strPropertyLocation = item[0]
strQuestion = item[1]
# Update Database to the Location the user is now at #
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET current_location = ? WHERE id = ?", (strProperty, strTeamName))
# Send user a message telling the the Question, Location and Property #
await ctx.send(f':question: The question for {strProperty} at {strPropertyLocation} is: {strQuestion}')
##################
# Answer Command #
##################
@commands.command(aliases=['answ', 'a'])
@funRoleChannelCheck()
async def answer(self, ctx, *, strAnswer):
# Declare some key variables #
strGuildID = str(ctx.guild.id)
strTeamName = str(self.funTeamRole(ctx))
# Get teams current location #
dbcursor.execute(f"SELECT current_location FROM tbl_{strGuildID} WHERE id = ?", (strTeamName, ))
lisCurrentLocation = dbcursor.fetchall()
tupCurrentLocation = lisCurrentLocation[0]
strCurrentLocation = tupCurrentLocation[0]
# Check if team has said they are going to visit the property #
if strCurrentLocation not in self.lisProperties:
if strCurrentLocation == '':
await ctx.send(':tophat: You need to use goto first to tell the game where your going! ```mr goto brown1```')
return None
# Set property to location #
strProperty = strCurrentLocation
# Get teams money and if they have visited this property from the database #
dbcursor.execute(f"SELECT money, {strProperty}_visited FROM tbl_{strGuildID} WHERE id = ?", (strTeamName, ))
lisMoneyVisted = dbcursor.fetchall()
for item in lisMoneyVisted:
intTeamsMoney = item[0]
strVisted = item[1]
# Check if the team has already visited that property #
if strVisted == 'Y':
raise MonopolyRunError.AlreadyVisted(strProperty)
# Check if a team owns the property #
dbcursor.execute(f"SELECT id FROM tbl_{strGuildID} WHERE {strProperty}_owner = 'Y'")
lisOwner = dbcursor.fetchall()
if not lisOwner:
strOwner = None
else:
tupOwner = lisOwner[0]
strOwner = tupOwner[0]
# Get how much money the owner has #
dbcursor.execute(f"SELECT money FROM tbl_{strGuildID} WHERE id = ?", (strOwner, ))
lisOwnersMoney = dbcursor.fetchall()
tupOwnersMoney = lisOwnersMoney[0]
intOwnersMoney = tupOwnersMoney[0]
# Get which set of Questions the guild is using #
dbcursor.execute("SELECT questions FROM tbl_guilds WHERE id = ?", (strGuildID, ))
lisQuestions = dbcursor.fetchall()
tupQuestions = lisQuestions[0]
strQuestions = tupQuestions[0]
# Get answer and value from database #
dbcursor.execute(f"SELECT answer, value FROM tbl_{strQuestions} WHERE id = ?", (strProperty, ))
lisOwnerAnswer = dbcursor.fetchall()
for item in lisOwnerAnswer:
strCorrectAnswers = item[0]
intValue = item[1]
# Check if a set of properties is owned by one team #
lisPropertiesToSelect = [i for i in self.lisProperties if i.startswith(re.sub('[0-9]+', '', strProperty))]
intNumberofPropertiesToSelect = len(lisPropertiesToSelect)
if intNumberofPropertiesToSelect == 2:
dbcursor.execute(f"SELECT id FROM tbl_{strGuildID} WHERE {lisPropertiesToSelect[0]}_owner = 'Y' and {lisPropertiesToSelect[1]}_owner = 'Y'")
lisPropertiesInSet = dbcursor.fetchall()
if not lisPropertiesInSet:
blnDoubleRent = False
else:
blnDoubleRent = True
elif intNumberofPropertiesToSelect == 3:
dbcursor.execute(f"SELECT id FROM tbl_{strGuildID} WHERE {lisPropertiesToSelect[0]}_owner = 'Y' and {lisPropertiesToSelect[1]}_owner = 'Y' and {lisPropertiesToSelect[2]}_owner = 'Y'")
lisPropertiesInSet = dbcursor.fetchall()
if not lisPropertiesInSet:
blnDoubleRent = False
else:
blnDoubleRent = True
elif intNumberofPropertiesToSelect == 4:
dbcursor.execute(f"SELECT id FROM tbl_{strGuildID} WHERE {lisPropertiesToSelect[0]}_owner = 'Y' and {lisPropertiesToSelect[1]}_owner = 'Y' and {lisPropertiesToSelect[2]}_owner = 'Y' and {lisPropertiesToSelect[3]}_owner = 'Y'", ())
lisPropertiesInSet = dbcursor.fetchall()
if not lisPropertiesInSet:
blnDoubleRent = False
else:
blnDoubleRent = True
else:
blnDoubleRent = False
# Check if answer is correct #
lisCorrectAnswers = strCorrectAnswers.split('-')
for item in lisCorrectAnswers:
# Convert to lowercase and remove spaces #
strCorrectAnswer = item.lower()
strCorrectAnswer = re.sub("\s+", '', strCorrectAnswer)
strAnswer = strAnswer.lower()
strAnswer = re.sub("\s+", '', strAnswer)
# Use fuzzy-wuzzy to compare the users answer with the correct answer #
intPartialRatio = fuzz.partial_ratio(strCorrectAnswer, strAnswer)
intTokenSetRatio = fuzz.token_set_ratio(strCorrectAnswer, strAnswer)
if intPartialRatio >= 80 or intTokenSetRatio >= 80:
blnAnswerCorrect = True
break
else:
blnAnswerCorrect = False
# Checks #
# Answer is correct AND the property is NOT owned #
if blnAnswerCorrect is True and strOwner is None and blnDoubleRent is False:
# Update property visited in database #
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET {strProperty}_visited = 'Y' WHERE id = ?", (strTeamName, ))
# Update teams money and set owner #
intUpdatedMoney = intTeamsMoney + intValue
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET money = '{intUpdatedMoney}', {strProperty}_owner = 'Y' WHERE id = '{strTeamName}'")
# Notify announcements of the Owner #
catMonopolyRun = utils.get(ctx.guild.categories, name="Monopoly Run")
chaAnnouncementChannel = utils.get(ctx.guild.channels, name="announcements", category_id=catMonopolyRun.id)
await chaAnnouncementChannel.send(f':house: {strTeamName} now owns {strProperty}!')
await ctx.send(f':white_check_mark: You now own {strProperty}!')
# Set current_location to empty #
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET current_location = ? WHERE id = ?", ("", strTeamName))
# Answer is correct AND the property is owned #
elif blnAnswerCorrect is True and strOwner is not None and blnDoubleRent is False:
# Update property visited in database #
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET {strProperty}_visited = 'Y' WHERE id = ?", (strTeamName, ))
# Update teams money #
intUpdatedMoney = intTeamsMoney + intValue
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET money = ? WHERE id = ?", (intUpdatedMoney, strTeamName))
# Update owners money #
chaOwner = utils.get(ctx.guild.channels, name=strOwner)
await chaOwner.send(f':dollar: {strTeamName} just paid £{intValue} on {strProperty}!')
intUpdatedMoney = intOwnersMoney + intValue
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET money = ? WHERE id = ?", (intUpdatedMoney, strOwner))
await ctx.send(f':white_check_mark: However since: {strOwner} already owns that property you paid: £{intValue} in rent!')
# Set current_location to empty #
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET current_location = ? WHERE id = ?", ("", strTeamName))
# Answer is correct AND the property is owned AND double rent #
elif blnAnswerCorrect is True and strOwner is not None and blnDoubleRent is True:
# Update property visited in database #
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET {strProperty}_visited = 'Y' WHERE id = ?", (strTeamName, ))
# Update teams money #
intUpdatedMoney = intTeamsMoney + intValue
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET money = ? WHERE id = ?", (intUpdatedMoney, strTeamName))
# Update owners money #
chaOwner = utils.get(ctx.guild.channels, name=strOwner)
await chaOwner.send(f':dollar: {strTeamName} just paid £{intValue * 2} on {strProperty}!')
intUpdatedMoney = intOwnersMoney + (intValue * 2)
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET money = ? WHERE id = ?", (intUpdatedMoney, strOwner))
await ctx.send(f':white_check_mark: However since: {strOwner} already owns that property and the other properties in the same group you paid: £{intValue*2} in rent!')
# Set current_location to empty #
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET current_location = ? WHERE id = ?", ("", strTeamName))
# Answer is incorrect AND the property is NOT owned #
elif blnAnswerCorrect is False and strOwner is None and blnDoubleRent is False:
await ctx.send(':negative_squared_cross_mark: Try again!')
# Answer is incorrect AND the property is owned #
elif blnAnswerCorrect is False and strOwner is not None and blnDoubleRent is False:
await ctx.send(f':negative_squared_cross_mark: Try again! However since: {strOwner} already owns that property you paid: £{intValue} in rent!')
# Update owners money #
chaOwner = utils.get(ctx.guild.channels, name=strOwner)
await chaOwner.send(f':dollar: {strTeamName} just paid £{intValue} on {strProperty}, even though they got the answer incorrect!')
intUpdatedMoney = intOwnersMoney + intValue
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET money = ? WHERE id = ?", (intUpdatedMoney, strOwner))
# Answer is incorrect AND the property is owned AND double rent #
elif blnAnswerCorrect is False and strOwner is not None and blnDoubleRent is True:
await ctx.send(f':negative_squared_cross_mark: Try again! However since: {strOwner} already owns that property and the other properties in the same group you paid: £{intValue*2} in rent!')
# Update owners money #
chaOwner = utils.get(ctx.guild.channels, name=strOwner)
await chaOwner.send(f':dollar: {strTeamName} just paid £{intValue * 2} on {strProperty}, even though they got the answer incorrect!')
intUpdatedMoney = intOwnersMoney + (intValue * 2)
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET money = ? WHERE id = ?", (intUpdatedMoney, strOwner))
# Error #
else:
# Set current_location to empty #
dbcursor.execute(f"UPDATE tbl_{strGuildID} SET current_location = ? WHERE id = ?", ("", strTeamName))
logging.error(f'Unexpected error: Answer, Correct Answer, Answer Correct, Owner, Double Rent, Afford is not valid! {strAnswer, strCorrectAnswer, blnAnswerCorrect, strOwner, blnDoubleRent, }')
await ctx.send(f':satellite: An unexpected error occurred! ```The error is: Answer, Correct Answer, Answer Correct, Owner, Double Rent, Afford is not valid! {strAnswer, strCorrectAnswer, blnAnswerCorrect, strOwner, blnDoubleRent, }``` ')
#################
# Money Command #
#################
@commands.command()
@funRoleChannelCheck()
async def money(self, ctx):
# Declare some key variables #
strGuildID = str(ctx.guild.id)
strTeamName = str(self.funTeamRole(ctx))
# Get money from database #
dbcursor.execute(f"SELECT money FROM tbl_{strGuildID} WHERE id = ?", (strTeamName, ))
lisMoney = dbcursor.fetchall()
tupMoney = lisMoney[0]
intMoney = tupMoney[0]
# Notify the user #
await ctx.send(f':dollar: You have £{intMoney}')
##########################
# Property Owner Command #
##########################
@commands.command()
@funRoleChannelCheck()
async def owner(self, ctx, *, strProperty: funToLower):
# Declare some key variables #
strGuildID = str(ctx.guild.id)
# Check if property exists but try and guess #
if strProperty not in self.lisProperties:
for strValidProperty in self.lisProperties:
intPartialRatio = fuzz.partial_ratio(strValidProperty, strProperty)
intTokenSetRatio = fuzz.token_set_ratio(strValidProperty, strProperty)
if intTokenSetRatio >= 80 or intTokenSetRatio >= 80:
await ctx.send(f':tophat: Could not find property however I have guessed you want: {strValidProperty}')
strProperty = strValidProperty
break
# Check if property exists #
if strProperty not in self.lisProperties:
raise MonopolyRunError.InvalidPropertyName(strProperty)
# Check if a team owns the property #
dbcursor.execute(f"SELECT id FROM tbl_{strGuildID} WHERE {strProperty}_owner = 'Y'")
lisOwner = dbcursor.fetchall()
if not lisOwner:
await ctx.send(f':house: {strProperty} is not owned by anyone!')
else:
tupOwner = lisOwner[0]
strOwner = tupOwner[0]
await ctx.send(f':house: {strProperty} is owned by {strOwner}!')
##################
# Error Handling #
##################
async def cog_command_error(self, ctx, error):
# Missing Role #
if isinstance(error, commands.MissingRole):
await ctx.send(':no_entry: You must have a team role! For example role: team1')
# Channel Not Found #
elif isinstance(error, commands.ChannelNotFound):
await ctx.send(':no_entry: You must use this command in you team channel e.g. team1!')
# Missing Required Argument #
elif isinstance(error, commands.MissingRequiredArgument):
if ctx.command.name == 'answer':
await ctx.send(f':no_entry: You need to specify a answer! ```mr {ctx.command.name} hello world```')
else:
await ctx.send(f':no_entry: You need to specify the property! e.g. ```mr {ctx.command.name} brown1```')
# No Private Message #
elif isinstance(error, commands.NoPrivateMessage):
await ctx.author.send(':no_entry: Please use all commands in a Server (Not Direct Messages)!')
# Invalid Property Name #
elif isinstance(error, MonopolyRunError.InvalidPropertyName):
await ctx.send(f':no_entry: Please enter a valid property! e.g. ```mr {ctx.command.name} brown1``` For a list of properties see the properties channel!')
# Not In Team Channel #
elif isinstance(error, MonopolyRunError.NotInTeamChannel):
await ctx.send(':no_entry: Please make sure you have the correct role for the correct team channel! e.g.```role team1 for channel team1```')
# Already visited #
elif isinstance(error, MonopolyRunError.AlreadyVisted):
await ctx.send(':no_entry: You have already answered the question for that property correctly!')
# Database Table Not Found #
elif isinstance(error, MonopolyRunError.DatabaseTableNotFound):
await ctx.send(':no_entry: No table was found in the database for this Server! Have you run setup?')
# Any other error #
else:
logging.error(f'Unexpected error: {error}')
await ctx.send(f':satellite: (Main)An unexpected error occurred! ```The error is: {error}``` ')