This commit is contained in:
2025-10-28 15:42:26 +01:00
parent 020c686c81
commit 18d7f318f6
44 changed files with 7168 additions and 7137 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -13,306 +13,306 @@ from gwendolyn_old.exceptions import GameNotInDatabase
from gwendolyn_old.utils import encode_id
class GameBase():
"""The base class for the games."""
"""The base class for the games."""
def __init__(self, bot, game_name: int, drawer):
"""Initialize the class."""
self.bot = bot
self.long_strings = self.bot.long_strings
self.resources = "gwendolyn/resources/games/"
self.game_name = game_name
self.draw = drawer(bot, self)
def __init__(self, bot, game_name: int, drawer):
"""Initialize the class."""
self.bot = bot
self.long_strings = self.bot.long_strings
self.resources = "gwendolyn/resources/games/"
self.game_name = game_name
self.draw = drawer(bot, self)
def _get_action_rows(self, buttons: list[tuple[str, list]]):
self.bot.log("Generation action rows")
button_objects = []
for label, data, style in buttons:
custom_id = encode_id([self.game_name] + data)
button_objects.append(Button(
style=style,
label=label,
custom_id=custom_id
))
def _get_action_rows(self, buttons: list[tuple[str, list]]):
self.bot.log("Generation action rows")
button_objects = []
for label, data, style in buttons:
custom_id = encode_id([self.game_name] + data)
button_objects.append(Button(
style=style,
label=label,
custom_id=custom_id
))
action_rows = []
for i in range(((len(button_objects)-1)//5)+1):
action_rows.append(ActionRow(*button_objects[i*5:i*5+5]))
action_rows = []
for i in range(((len(button_objects)-1)//5)+1):
action_rows.append(ActionRow(*button_objects[i*5:i*5+5]))
return action_rows
return action_rows
async def _send_image(self, channel: Messageable,
buttons: list[tuple[str, list]] = None, delete=True):
self.draw.draw(str(channel.id))
file_path = f"{self.resources}images/{self.game_name}{channel.id}.png"
old_image = await channel.send(
file=File(file_path), delete_after=120 if delete else None)
async def _send_image(self, channel: Messageable,
buttons: list[tuple[str, list]] = None, delete=True):
self.draw.draw(str(channel.id))
file_path = f"{self.resources}images/{self.game_name}{channel.id}.png"
old_image = await channel.send(
file=File(file_path), delete_after=120 if delete else None)
if buttons is not None and len(buttons) < 25:
await old_image.edit(components = self._get_action_rows(buttons))
if buttons is not None and len(buttons) < 25:
await old_image.edit(components = self._get_action_rows(buttons))
return old_image
return old_image
class DatabaseGame(GameBase):
"""The base class for the games."""
"""The base class for the games."""
def __init__(self, bot, game_name, drawer):
"""Initialize the class."""
super().__init__(bot, game_name, drawer)
self.database = self.bot.database
self.old_images_path = f"{self.resources}old_images/{self.game_name}"
def __init__(self, bot, game_name, drawer):
"""Initialize the class."""
super().__init__(bot, game_name, drawer)
self.database = self.bot.database
self.old_images_path = f"{self.resources}old_images/{self.game_name}"
def access_document(self, channel: str, collection_name: str="games",
raise_missing_error: bool=True):
collection = self.bot.database[f"{self.game_name} {collection_name}"]
game = collection.find_one({"_id": channel})
if game is None and raise_missing_error:
raise GameNotInDatabase(self.game_name, channel)
def access_document(self, channel: str, collection_name: str="games",
raise_missing_error: bool=True):
collection = self.bot.database[f"{self.game_name} {collection_name}"]
game = collection.find_one({"_id": channel})
if game is None and raise_missing_error:
raise GameNotInDatabase(self.game_name, channel)
return game
return game
def _test_document(self, channel: str, collection_name: str="games"):
collection = self.bot.database[f"{self.game_name} {collection_name}"]
game = collection.find_one({"_id": channel})
return game is not None
def _test_document(self, channel: str, collection_name: str="games"):
collection = self.bot.database[f"{self.game_name} {collection_name}"]
game = collection.find_one({"_id": channel})
return game is not None
def _update_document(self, channel: str, updater: dict,
collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].update_one(
{"_id": channel},
updater,
upsert=True
)
def _update_document(self, channel: str, updater: dict,
collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].update_one(
{"_id": channel},
updater,
upsert=True
)
def _delete_document(self, channel: str, collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].delete_one(
{"_id": channel}
)
def _delete_document(self, channel: str, collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].delete_one(
{"_id": channel}
)
def _insert_document(self, data: dict, collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].insert_one(data)
def _insert_document(self, data: dict, collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].insert_one(data)
async def _delete_old_image(self, channel: Messageable):
with open(self.old_images_path + str(channel.id), "r") as file_pointer:
old_image = await channel.fetch_message(int(file_pointer.read()))
async def _delete_old_image(self, channel: Messageable):
with open(self.old_images_path + str(channel.id), "r") as file_pointer:
old_image = await channel.fetch_message(int(file_pointer.read()))
await old_image.delete()
await old_image.delete()
async def _send_image(self, channel: Messageable,
buttons: list[tuple[str, list]] = None, delete=True):
old_image = await super()._send_image(channel, buttons, delete)
with open(self.old_images_path + str(channel.id), "w") as file_pointer:
file_pointer.write(str(old_image.id))
async def _send_image(self, channel: Messageable,
buttons: list[tuple[str, list]] = None, delete=True):
old_image = await super()._send_image(channel, buttons, delete)
with open(self.old_images_path + str(channel.id), "w") as file_pointer:
file_pointer.write(str(old_image.id))
async def _start_new(self, channel: Messageable, new_game: dict,
buttons: list[tuple[str, list]] = None,
delete = True):
new_game['_id'] = str(channel.id)
self._insert_document(new_game)
await self._send_image(channel, buttons, delete)
async def _start_new(self, channel: Messageable, new_game: dict,
buttons: list[tuple[str, list]] = None,
delete = True):
new_game['_id'] = str(channel.id)
self._insert_document(new_game)
await self._send_image(channel, buttons, delete)
async def _end_game(self, channel: Messageable):
await self._delete_old_image(channel)
await self._send_image(channel, delete=False)
self._delete_document(str(channel.id))
async def _end_game(self, channel: Messageable):
await self._delete_old_image(channel)
await self._send_image(channel, delete=False)
self._delete_document(str(channel.id))
class CardGame(DatabaseGame):
"""The class for card games."""
def __init__(self, bot, game_name, drawer, deck_used):
"""Initialize the class."""
super().__init__(bot, game_name, drawer)
self.decks_used = deck_used
"""The class for card games."""
def __init__(self, bot, game_name, drawer, deck_used):
"""Initialize the class."""
super().__init__(bot, game_name, drawer)
self.decks_used = deck_used
def _shuffle_cards(self, channel: str):
self.bot.log(f"Shuffling cards for {self.game_name}")
with open(f"{self.resources}deck_of_cards.txt", "r") as file_pointer:
deck = file_pointer.read()
def _shuffle_cards(self, channel: str):
self.bot.log(f"Shuffling cards for {self.game_name}")
with open(f"{self.resources}deck_of_cards.txt", "r") as file_pointer:
deck = file_pointer.read()
all_decks = deck.split("\n") * self.decks_used
random.shuffle(all_decks)
all_decks = deck.split("\n") * self.decks_used
random.shuffle(all_decks)
self._update_document(
channel,
{"$set": {"_id": channel, "cards": all_decks}},
"cards"
)
self._update_document(
channel,
{"$set": {"_id": channel, "cards": all_decks}},
"cards"
)
def _draw_card(self, channel: str):
"""
Draw a card from the stack.
def _draw_card(self, channel: str):
"""
Draw a card from the stack.
*Parameters*
------------
channel: str
The id of the channel the card is drawn in.
"""
self.bot.log("drawing a card")
*Parameters*
------------
channel: str
The id of the channel the card is drawn in.
"""
self.bot.log("drawing a card")
drawn_card = self.access_document(channel, "cards")["cards"][0]
self._update_document(channel, {"$pop": {"cards": -1}}, "cards")
drawn_card = self.access_document(channel, "cards")["cards"][0]
self._update_document(channel, {"$pop": {"cards": -1}}, "cards")
return drawn_card
return drawn_card
class BoardGame(GameBase):
async def _test_opponent(self, ctx: IntCont, opponent: Union[int, User]):
if isinstance(opponent, int):
# Opponent is Gwendolyn
if opponent in range(1, 6):
difficulty = int(opponent)
difficulty_text = f" with difficulty {difficulty}"
opponent = self.bot.user.id
else:
await ctx.send("Difficulty doesn't exist")
self.bot.log("They challenged a difficulty that doesn't exist")
return False
elif isinstance(opponent, User):
if opponent.bot:
# User has challenged a bot
if opponent == self.bot.user:
# It was Gwendolyn
difficulty = 3
difficulty_text = f" with difficulty {difficulty}"
opponent = self.bot.user.id
else:
await ctx.send("You can't challenge a bot!")
self.bot.log("They tried to challenge a bot")
return False
else:
# Opponent is another player
if ctx.author != opponent:
opponent = opponent.id
difficulty = 5
difficulty_text = ""
else:
await ctx.send("You can't play against yourself")
self.bot.log("They tried to play against themself")
return False
async def _test_opponent(self, ctx: IntCont, opponent: Union[int, User]):
if isinstance(opponent, int):
# Opponent is Gwendolyn
if opponent in range(1, 6):
difficulty = int(opponent)
difficulty_text = f" with difficulty {difficulty}"
opponent = self.bot.user.id
else:
await ctx.send("Difficulty doesn't exist")
self.bot.log("They challenged a difficulty that doesn't exist")
return False
elif isinstance(opponent, User):
if opponent.bot:
# User has challenged a bot
if opponent == self.bot.user:
# It was Gwendolyn
difficulty = 3
difficulty_text = f" with difficulty {difficulty}"
opponent = self.bot.user.id
else:
await ctx.send("You can't challenge a bot!")
self.bot.log("They tried to challenge a bot")
return False
else:
# Opponent is another player
if ctx.author != opponent:
opponent = opponent.id
difficulty = 5
difficulty_text = ""
else:
await ctx.send("You can't play against yourself")
self.bot.log("They tried to play against themself")
return False
return opponent, (difficulty, difficulty_text)
return opponent, (difficulty, difficulty_text)
class BaseDrawer():
"""Class for drawing games."""
def __init__(self, bot, game: GameBase):
self.bot = bot
self.game = game
self.fonts_path = "gwendolyn/resources/fonts/"
self.font_name = "futura-bold"
"""Class for drawing games."""
def __init__(self, bot, game: GameBase):
self.bot = bot
self.game = game
self.fonts_path = "gwendolyn/resources/fonts/"
self.font_name = "futura-bold"
self.resources = game.resources
game_name = game.game_name
self.default_image = f"{self.resources}default_images/{game_name}.png"
self.default_size = None
self.default_color = None
self.images_path = f"{self.resources}images/{game_name}"
self.resources = game.resources
game_name = game.game_name
self.default_image = f"{self.resources}default_images/{game_name}.png"
self.default_size = None
self.default_color = None
self.images_path = f"{self.resources}images/{game_name}"
def _get_size(self, game: dict):
return self.default_size
def _get_size(self, game: dict):
return self.default_size
def _draw_image(self, game: dict, image: Image.Image):
pass
def _draw_image(self, game: dict, image: Image.Image):
pass
def draw(self, channel: str):
game = self.game.access_document(channel)
if self.default_image is not None:
image = Image.open(self.default_image)
else:
image = Image.new("RGB", self._get_size(game), self.default_color)
self._draw_image(game, image)
self._save_image(image, channel)
def draw(self, channel: str):
game = self.game.access_document(channel)
if self.default_image is not None:
image = Image.open(self.default_image)
else:
image = Image.new("RGB", self._get_size(game), self.default_color)
self._draw_image(game, image)
self._save_image(image, channel)
def _get_font(self, size: int, font_name: str = None):
if font_name is None:
font_name = self.font_name
return ImageFont.truetype(f"{self.fonts_path}{font_name}.ttf", size)
def _get_font(self, size: int, font_name: str = None):
if font_name is None:
font_name = self.font_name
return ImageFont.truetype(f"{self.fonts_path}{font_name}.ttf", size)
def _adjust_font(self, max_font_size: int, text: str, max_text_size: int,
font_name: str = None):
font_size = max_font_size
font = self._get_font(font_size, font_name)
text_width = font.getsize(text)[0]
def _adjust_font(self, max_font_size: int, text: str, max_text_size: int,
font_name: str = None):
font_size = max_font_size
font = self._get_font(font_size, font_name)
text_width = font.getsize(text)[0]
while text_width > max_text_size:
font_size -= 1
font = self._get_font(font_size, font_name)
text_width = font.getsize(text)[0]
while text_width > max_text_size:
font_size -= 1
font = self._get_font(font_size, font_name)
text_width = font.getsize(text)[0]
return font, font_size
return font, font_size
def _save_image(self, image: Image.Image, channel: str):
self.bot.log("Saving image")
image.save(f"{self.images_path}{channel}.png")
def _save_image(self, image: Image.Image, channel: str):
self.bot.log("Saving image")
image.save(f"{self.images_path}{channel}.png")
def _draw_shadow_text(self, text: str, colors: list[tuple[int, int, int]],
font_size: int):
font = self._get_font(font_size)
offset = font_size//20
shadow_offset = font_size//10
def _draw_shadow_text(self, text: str, colors: list[tuple[int, int, int]],
font_size: int):
font = self._get_font(font_size)
offset = font_size//20
shadow_offset = font_size//10
text_size = list(font.getsize(text))
text_size[0] += 1 + (offset * 2)
text_size[1] += 1 + (offset * 2) + shadow_offset
text_size = list(font.getsize(text))
text_size[0] += 1 + (offset * 2)
text_size[1] += 1 + (offset * 2) + shadow_offset
image = Image.new("RGBA", tuple(text_size), (0, 0, 0, 0))
text_image = ImageDraw.Draw(image)
image = Image.new("RGBA", tuple(text_size), (0, 0, 0, 0))
text_image = ImageDraw.Draw(image)
for color_index, color in enumerate(colors[:-1]):
color_offset = offset//(color_index+1)
if color_index == 0:
color_shadow_offset = shadow_offset
else:
color_shadow_offset = 0
for color_index, color in enumerate(colors[:-1]):
color_offset = offset//(color_index+1)
if color_index == 0:
color_shadow_offset = shadow_offset
else:
color_shadow_offset = 0
for i in range(4):
x_pos = [-1,1][i % 2] * color_offset
y_pos = [-1,1][(i//2) % 2] * color_offset + color_shadow_offset
position = (offset + x_pos, offset + y_pos)
text_image.text(position, text, fill=color, font=font)
for i in range(4):
x_pos = [-1,1][i % 2] * color_offset
y_pos = [-1,1][(i//2) % 2] * color_offset + color_shadow_offset
position = (offset + x_pos, offset + y_pos)
text_image.text(position, text, fill=color, font=font)
text_image.text((offset, offset), text, fill=colors[-1], font=font)
text_image.text((offset, offset), text, fill=colors[-1], font=font)
return image
return image
class CardDrawer(BaseDrawer):
def __init__(self, bot, game: GameBase):
super().__init__(bot, game)
self.cards_path = f"{self.resources}cards/"
def __init__(self, bot, game: GameBase):
super().__init__(bot, game)
self.cards_path = f"{self.resources}cards/"
# pylint: disable=invalid-name
self.CARD_WIDTH = 691
self.CARD_HEIGHT = 1065
# pylint: disable=invalid-name
self.CARD_WIDTH = 691
self.CARD_HEIGHT = 1065
self.CARD_BORDER = 100
self.CARD_OFFSET = 125
# pylint: enable=invalid-name
self.CARD_BORDER = 100
self.CARD_OFFSET = 125
# pylint: enable=invalid-name
def _set_card_size(self, wanted_card_width: int):
ratio = wanted_card_width / self.CARD_WIDTH
self.CARD_WIDTH = wanted_card_width
self.CARD_HEIGHT = int(self.CARD_HEIGHT * ratio)
self.CARD_BORDER = int(self.CARD_BORDER * ratio)
self.CARD_OFFSET = int(self.CARD_OFFSET * ratio)
def _set_card_size(self, wanted_card_width: int):
ratio = wanted_card_width / self.CARD_WIDTH
self.CARD_WIDTH = wanted_card_width
self.CARD_HEIGHT = int(self.CARD_HEIGHT * ratio)
self.CARD_BORDER = int(self.CARD_BORDER * ratio)
self.CARD_OFFSET = int(self.CARD_OFFSET * ratio)
def _draw_cards(self, hand: list[str], hidden_cards: int = 0):
image_width = (self.CARD_BORDER * 2) + self.CARD_WIDTH
image_width += (self.CARD_OFFSET * (len(hand)-1))
image_size = (image_width, (self.CARD_BORDER * 2) + self.CARD_HEIGHT)
background = Image.new("RGBA", image_size, (0, 0, 0, 0))
def _draw_cards(self, hand: list[str], hidden_cards: int = 0):
image_width = (self.CARD_BORDER * 2) + self.CARD_WIDTH
image_width += (self.CARD_OFFSET * (len(hand)-1))
image_size = (image_width, (self.CARD_BORDER * 2) + self.CARD_HEIGHT)
background = Image.new("RGBA", image_size, (0, 0, 0, 0))
for i, card in enumerate(hand):
position = (
self.CARD_BORDER + (self.CARD_OFFSET*i),
self.CARD_BORDER
)
for i, card in enumerate(hand):
position = (
self.CARD_BORDER + (self.CARD_OFFSET*i),
self.CARD_BORDER
)
if i + hidden_cards < len(hand):
card_image = Image.open(f"{self.cards_path}{card.upper()}.png")
else:
card_image = Image.open(f"{self.cards_path}red_back.png")
if i + hidden_cards < len(hand):
card_image = Image.open(f"{self.cards_path}{card.upper()}.png")
else:
card_image = Image.open(f"{self.cards_path}red_back.png")
card_image = card_image.resize(
(self.CARD_WIDTH, self.CARD_HEIGHT),
resample=Image.BILINEAR
)
background.paste(card_image, position, card_image)
card_image = card_image.resize(
(self.CARD_WIDTH, self.CARD_HEIGHT),
resample=Image.BILINEAR
)
background.paste(card_image, position, card_image)
return background
return background

View File

@@ -3,8 +3,8 @@ Has a container for game functions.
*Classes*
---------
Games
Container for game functions.
Games
Container for game functions.
"""
@@ -17,30 +17,30 @@ from .wordle import WordleGame
class Games():
"""
Contains game classes.
"""
Contains game classes.
*Attributes*
------------
bot: Gwendolyn
The instance of Gwendolyn.
blackjack
Contains blackjack functions.
connect_four
Contains connect four functions.
hangman
Contains hangman functions.
hex
Contains hex functions
"""
*Attributes*
------------
bot: Gwendolyn
The instance of Gwendolyn.
blackjack
Contains blackjack functions.
connect_four
Contains connect four functions.
hangman
Contains hangman functions.
hex
Contains hex functions
"""
def __init__(self, bot):
"""Initialize the container."""
self.bot = bot
def __init__(self, bot):
"""Initialize the container."""
self.bot = bot
self.trivia = Trivia(bot)
self.blackjack = Blackjack(bot)
self.connect_four = ConnectFour(bot)
self.hangman = Hangman(bot)
self.hex = HexGame(bot)
self.wordle = WordleGame(bot)
self.trivia = Trivia(bot)
self.blackjack = Blackjack(bot)
self.connect_four = ConnectFour(bot)
self.hangman = Hangman(bot)
self.hex = HexGame(bot)
self.wordle = WordleGame(bot)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,149 +3,149 @@ Contains the code that deals with money.
*Classes*
---------
Money
Deals with money.
Money
Deals with money.
"""
import interactions # Used for typehints
import discord # Used for typehints
class Money():
"""
Deals with money.
*Methods*
---------
checkBalance(user: str)
sendBalance(ctx: interactions.SlashContext)
addMoney(user: str, amount: int)
giveMoney(ctx: interactions.SlashContext, user: discord.User,
amount: int)
*Attributes*
------------
bot: Gwendolyn
The instance of Gwendolyn
database: pymongo.Client
The mongo database
"""
def __init__(self, bot):
"""Initialize the class."""
self.bot = bot
self.database = bot.database
def checkBalance(self, user: str):
"""
Deals with money.
Get the account balance of a user.
*Methods*
---------
checkBalance(user: str)
sendBalance(ctx: interactions.SlashContext)
addMoney(user: str, amount: int)
giveMoney(ctx: interactions.SlashContext, user: discord.User,
amount: int)
*Attributes*
*Parameters*
------------
bot: Gwendolyn
The instance of Gwendolyn
database: pymongo.Client
The mongo database
user: str
The user to get the balance of.
*Returns*
---------
balance: int
The balance of the user's account.
"""
self.bot.log("checking "+user+"'s account balance")
def __init__(self, bot):
"""Initialize the class."""
self.bot = bot
self.database = bot.database
user_data = self.database["users"].find_one({"_id": user})
def checkBalance(self, user: str):
"""
Get the account balance of a user.
if user_data is not None:
return user_data["money"]
else:
return 0
*Parameters*
------------
user: str
The user to get the balance of.
async def sendBalance(self, ctx: interactions.SlashContext):
"""
Get your own account balance.
*Returns*
---------
balance: int
The balance of the user's account.
"""
self.bot.log("checking "+user+"'s account balance")
*Parameters*
------------
ctx: interactions.SlashContext
The context of the command.
"""
await self.bot.defer(ctx)
response = self.checkBalance("#"+str(ctx.author.id))
user_name = ctx.author.display_name
if response == 1:
new_message = f"{user_name} has {response} GwendoBuck"
else:
new_message = f"{user_name} has {response} GwendoBucks"
await ctx.send(new_message)
user_data = self.database["users"].find_one({"_id": user})
# Adds money to the account of a user
def addMoney(self, user: str, amount: int):
"""
Add money to a user account.
if user_data is not None:
return user_data["money"]
else:
return 0
*Parameters*
------------
user: str
The id of the user to give money.
amount: int
The amount to add to the user's account.
"""
self.bot.log("adding "+str(amount)+" to "+user+"'s account")
async def sendBalance(self, ctx: interactions.SlashContext):
"""
Get your own account balance.
user_data = self.database["users"].find_one({"_id": user})
*Parameters*
------------
ctx: interactions.SlashContext
The context of the command.
"""
await self.bot.defer(ctx)
response = self.checkBalance("#"+str(ctx.author.id))
user_name = ctx.author.display_name
if response == 1:
new_message = f"{user_name} has {response} GwendoBuck"
else:
new_message = f"{user_name} has {response} GwendoBucks"
await ctx.send(new_message)
if user_data is not None:
updater = {"$inc": {"money": amount}}
self.database["users"].update_one({"_id": user}, updater)
else:
new_user = {
"_id": user,
"user name": self.bot.database_funcs.get_name(user),
"money": amount
}
self.database["users"].insert_one(new_user)
# Adds money to the account of a user
def addMoney(self, user: str, amount: int):
"""
Add money to a user account.
# Transfers money from one user to another
async def giveMoney(self, ctx: interactions.SlashContext,
user: discord.User, amount: int):
"""
Give someone else money from your account.
*Parameters*
------------
user: str
The id of the user to give money.
amount: int
The amount to add to the user's account.
"""
self.bot.log("adding "+str(amount)+" to "+user+"'s account")
*Parameters*
------------
ctx: interactions.SlashContext
The context of the command.
user: discord.User
The user to give money.
amount: int
The amount to transfer.
"""
await self.bot.defer(ctx)
username = user.display_name
if self.bot.database_funcs.get_id(username) is None:
async for member in ctx.guild.fetch_members(limit=None):
if member.display_name.lower() == username.lower():
username = member.display_name
user_id = f"#{member.id}"
new_user = {
"_id": user_id,
"user name": username,
"money": 0
}
self.bot.database["users"].insert_one(new_user)
user_data = self.database["users"].find_one({"_id": user})
userid = f"#{ctx.author.id}"
user_data = self.database["users"].find_one({"_id": userid})
targetUser = self.bot.database_funcs.get_id(username)
if user_data is not None:
updater = {"$inc": {"money": amount}}
self.database["users"].update_one({"_id": user}, updater)
else:
new_user = {
"_id": user,
"user name": self.bot.database_funcs.get_name(user),
"money": amount
}
self.database["users"].insert_one(new_user)
# Transfers money from one user to another
async def giveMoney(self, ctx: interactions.SlashContext,
user: discord.User, amount: int):
"""
Give someone else money from your account.
*Parameters*
------------
ctx: interactions.SlashContext
The context of the command.
user: discord.User
The user to give money.
amount: int
The amount to transfer.
"""
await self.bot.defer(ctx)
username = user.display_name
if self.bot.database_funcs.get_id(username) is None:
async for member in ctx.guild.fetch_members(limit=None):
if member.display_name.lower() == username.lower():
username = member.display_name
user_id = f"#{member.id}"
new_user = {
"_id": user_id,
"user name": username,
"money": 0
}
self.bot.database["users"].insert_one(new_user)
userid = f"#{ctx.author.id}"
user_data = self.database["users"].find_one({"_id": userid})
targetUser = self.bot.database_funcs.get_id(username)
if amount <= 0:
self.bot.log("They tried to steal")
await ctx.send("Yeah, no. You can't do that")
elif targetUser is None:
self.bot.log("They weren't in the system")
await ctx.send("The target doesn't exist")
elif user_data is None or user_data["money"] < amount:
self.bot.log("They didn't have enough GwendoBucks")
await ctx.send("You don't have that many GwendoBuck")
else:
self.addMoney(f"#{ctx.author.id}", -1 * amount)
self.addMoney(targetUser, amount)
await ctx.send(f"Transferred {amount} GwendoBucks to {username}")
if amount <= 0:
self.bot.log("They tried to steal")
await ctx.send("Yeah, no. You can't do that")
elif targetUser is None:
self.bot.log("They weren't in the system")
await ctx.send("The target doesn't exist")
elif user_data is None or user_data["money"] < amount:
self.bot.log("They didn't have enough GwendoBucks")
await ctx.send("You don't have that many GwendoBuck")
else:
self.addMoney(f"#{ctx.author.id}", -1 * amount)
self.addMoney(targetUser, amount)
await ctx.send(f"Transferred {amount} GwendoBucks to {username}")

View File

@@ -3,8 +3,8 @@ Contains code for trivia games.
*Classes*
---------
Trivia
Contains all the code for the trivia commands.
Trivia
Contains all the code for the trivia commands.
"""
import urllib # Used to get data from api
import json # Used to read data from api
@@ -15,191 +15,191 @@ from interactions import SlashContext # Used for type hints
class Trivia():
"""
Contains the code for trivia games.
"""
Contains the code for trivia games.
*Methods*
*Methods*
---------
triviaStart(channel: str) -> str, str, str
triviaAnswer(user: str, channel: str, command: str) -> str
triviaCountPoints(channel: str)
triviaParse(ctx: SlashContext, answer: str)
"""
def __init__(self, bot):
"""Initialize the class."""
self.bot = bot
def triviaStart(self, channel: str):
"""
Start a game of trivia.
Downloads a question with answers, shuffles the wrong answers
with the correct answer and returns the questions and answers.
Also saves the question in the database.
*Parameters*
------------
channel: str
The id of the channel to start the game in
*Returns*
---------
triviaStart(channel: str) -> str, str, str
triviaAnswer(user: str, channel: str, command: str) -> str
triviaCountPoints(channel: str)
triviaParse(ctx: SlashContext, answer: str)
send_message: str
The message to return to the user.
"""
triviaQuestions = self.bot.database["trivia questions"]
question = triviaQuestions.find_one({"_id": channel})
def __init__(self, bot):
"""Initialize the class."""
self.bot = bot
self.bot.log(f"Trying to find a trivia question for {channel}")
def triviaStart(self, channel: str):
"""
Start a game of trivia.
if question is None:
apiUrl = "https://opentdb.com/api.php?amount=10&type=multiple"
with urllib.request.urlopen(apiUrl) as response:
data = json.loads(response.read())
Downloads a question with answers, shuffles the wrong answers
with the correct answer and returns the questions and answers.
Also saves the question in the database.
question = data["results"][0]["question"]
self.bot.log(f"Found the question \"{question}\"")
answers = data["results"][0]["incorrect_answers"]
answers.append(data["results"][0]["correct_answer"])
random.shuffle(answers)
correctAnswer = data["results"][0]["correct_answer"]
correctAnswer = answers.index(correctAnswer) + 97
*Parameters*
------------
channel: str
The id of the channel to start the game in
newQuestion = {
"_id": channel,
"answer": str(chr(correctAnswer)),
"players": {}
}
triviaQuestions.insert_one(newQuestion)
*Returns*
---------
send_message: str
The message to return to the user.
"""
triviaQuestions = self.bot.database["trivia questions"]
question = triviaQuestions.find_one({"_id": channel})
replacements = {
"&#039;": "\'",
"&quot;": "\"",
"&ldquo;": "\"",
"&rdquo;": "\"",
"&eacute;": "é"
}
question = data["results"][0]["question"]
self.bot.log(f"Trying to find a trivia question for {channel}")
for key, value in replacements.items():
question = question.replace(key, value)
if question is None:
apiUrl = "https://opentdb.com/api.php?amount=10&type=multiple"
with urllib.request.urlopen(apiUrl) as response:
data = json.loads(response.read())
for answer in answers:
for key, value in replacements.items():
answer = answer.replace(key, value)
question = data["results"][0]["question"]
self.bot.log(f"Found the question \"{question}\"")
answers = data["results"][0]["incorrect_answers"]
answers.append(data["results"][0]["correct_answer"])
random.shuffle(answers)
correctAnswer = data["results"][0]["correct_answer"]
correctAnswer = answers.index(correctAnswer) + 97
return question, answers, correctAnswer
else:
log_message = "There was already a trivia question for that channel"
self.bot.log(log_message)
return self.bot.long_strings["Trivia going on"], "", ""
newQuestion = {
"_id": channel,
"answer": str(chr(correctAnswer)),
"players": {}
}
triviaQuestions.insert_one(newQuestion)
def triviaAnswer(self, user: str, channel: str, command: str):
"""
Answer the current trivia question.
replacements = {
"&#039;": "\'",
"&quot;": "\"",
"&ldquo;": "\"",
"&rdquo;": "\"",
"&eacute;": "é"
}
question = data["results"][0]["question"]
*Parameters*
------------
user: str
The id of the user who answered.
channel: str
The id of the channel the game is in.
command: str
The user's answer.
for key, value in replacements.items():
question = question.replace(key, value)
*Returns*
---------
send_message: str
The message to send if the function failed.
"""
triviaQuestions = self.bot.database["trivia questions"]
question = triviaQuestions.find_one({"_id": channel})
for answer in answers:
for key, value in replacements.items():
answer = answer.replace(key, value)
if command not in ["a", "b", "c", "d"]:
self.bot.log("I didn't quite understand that")
return "I didn't quite understand that"
elif question is None:
self.bot.log("There's no question right now")
return "There's no question right now"
elif user in question["players"]:
self.bot.log(f"{user} has already answered this question")
return f"{user} has already answered this question"
else:
self.bot.log(f"{user} answered the question in {channel}")
return question, answers, correctAnswer
else:
log_message = "There was already a trivia question for that channel"
self.bot.log(log_message)
return self.bot.long_strings["Trivia going on"], "", ""
updater = {"$set": {f"players.{user}": command}}
triviaQuestions.update_one({"_id": channel}, updater)
return None
def triviaAnswer(self, user: str, channel: str, command: str):
"""
Answer the current trivia question.
def triviaCountPoints(self, channel: str):
"""
Add money to every winner's account.
*Parameters*
------------
user: str
The id of the user who answered.
channel: str
The id of the channel the game is in.
command: str
The user's answer.
*Parameters*
------------
channel: str
The id of the channel the game is in.
"""
triviaQuestions = self.bot.database["trivia questions"]
question = triviaQuestions.find_one({"_id": channel})
*Returns*
---------
send_message: str
The message to send if the function failed.
"""
triviaQuestions = self.bot.database["trivia questions"]
question = triviaQuestions.find_one({"_id": channel})
self.bot.log("Counting points for question in "+channel)
if command not in ["a", "b", "c", "d"]:
self.bot.log("I didn't quite understand that")
return "I didn't quite understand that"
elif question is None:
self.bot.log("There's no question right now")
return "There's no question right now"
elif user in question["players"]:
self.bot.log(f"{user} has already answered this question")
return f"{user} has already answered this question"
else:
self.bot.log(f"{user} answered the question in {channel}")
if question is not None:
for player, answer in question["players"].items():
if answer == question["answer"]:
self.bot.money.addMoney(player, 1)
else:
self.bot.log("Couldn't find the questio")
updater = {"$set": {f"players.{user}": command}}
triviaQuestions.update_one({"_id": channel}, updater)
return None
return None
def triviaCountPoints(self, channel: str):
"""
Add money to every winner's account.
async def triviaParse(self, ctx: SlashContext, answer: str):
"""
Parse a trivia command.
*Parameters*
------------
channel: str
The id of the channel the game is in.
"""
triviaQuestions = self.bot.database["trivia questions"]
question = triviaQuestions.find_one({"_id": channel})
*Parameters*
------------
ctx: SlashContext
The context of the command.
answer: str
The answer, if any.
"""
await self.bot.defer(ctx)
channelId = str(ctx.channel_id)
if answer == "":
question, options, correctAnswer = self.triviaStart(channelId)
if options != "":
results = "**"+question+"**\n"
for x, option in enumerate(options):
results += chr(x+97) + ") "+option+"\n"
self.bot.log("Counting points for question in "+channel)
await ctx.send(results)
if question is not None:
for player, answer in question["players"].items():
if answer == question["answer"]:
self.bot.money.addMoney(player, 1)
else:
self.bot.log("Couldn't find the questio")
await asyncio.sleep(60)
return None
self.triviaCountPoints(channelId)
async def triviaParse(self, ctx: SlashContext, answer: str):
"""
Parse a trivia command.
delete_gameParams = ["trivia questions", channelId]
self.bot.database_funcs.delete_game(*delete_gameParams)
*Parameters*
------------
ctx: SlashContext
The context of the command.
answer: str
The answer, if any.
"""
await self.bot.defer(ctx)
channelId = str(ctx.channel_id)
if answer == "":
question, options, correctAnswer = self.triviaStart(channelId)
if options != "":
results = "**"+question+"**\n"
for x, option in enumerate(options):
results += chr(x+97) + ") "+option+"\n"
self.bot.log("Time's up for the trivia question", channelId)
send_message = self.bot.long_strings["Trivia time up"]
format_parameters = [chr(correctAnswer), options[correctAnswer-97]]
send_message = send_message.format(*format_parameters)
await ctx.send(send_message)
else:
await ctx.send(question, hidden=True)
await ctx.send(results)
await asyncio.sleep(60)
self.triviaCountPoints(channelId)
delete_gameParams = ["trivia questions", channelId]
self.bot.database_funcs.delete_game(*delete_gameParams)
self.bot.log("Time's up for the trivia question", channelId)
send_message = self.bot.long_strings["Trivia time up"]
format_parameters = [chr(correctAnswer), options[correctAnswer-97]]
send_message = send_message.format(*format_parameters)
await ctx.send(send_message)
else:
await ctx.send(question, hidden=True)
elif answer in ["a", "b", "c", "d"]:
userId = f"#{ctx.author.id}"
response = self.triviaAnswer(userId, channelId, answer)
if response is None:
user_name = ctx.author.display_name
await ctx.send(f"{user_name} answered **{answer}**")
else:
await ctx.send(response)
else:
self.bot.log("I didn't understand that", channelId)
await ctx.send("I didn't understand that")
elif answer in ["a", "b", "c", "d"]:
userId = f"#{ctx.author.id}"
response = self.triviaAnswer(userId, channelId, answer)
if response is None:
user_name = ctx.author.display_name
await ctx.send(f"{user_name} answered **{answer}**")
else:
await ctx.send(response)
else:
self.bot.log("I didn't understand that", channelId)
await ctx.send("I didn't understand that")

View File

@@ -25,177 +25,177 @@ PRESENT_COLOR = "#0f7aa8"
COLORS = [INCORRECT_COLOR, PRESENT_COLOR, CORRECT_COLOR]
class WordleGame(DatabaseGame):
"""Implementation of wordle game."""
def __init__(self, bot):
super().__init__(bot, "wordle", WordleDrawer)
self._API_url = "https://api.wordnik.com/v4/words.json/randomWords?" # pylint: disable=invalid-name
api_key = self.bot.credentials["wordnik_key"]
self._APIPARAMS = { # pylint: disable=invalid-name
"hasDictionaryDef": True,
"minCorpusCount": 5000,
"maxCorpusCount": -1,
"minDictionaryCount": 1,
"maxDictionaryCount": -1,
"limit": 1,
"api_key": api_key
}
"""Implementation of wordle game."""
def __init__(self, bot):
super().__init__(bot, "wordle", WordleDrawer)
self._API_url = "https://api.wordnik.com/v4/words.json/randomWords?" # pylint: disable=invalid-name
api_key = self.bot.credentials["wordnik_key"]
self._APIPARAMS = { # pylint: disable=invalid-name
"hasDictionaryDef": True,
"minCorpusCount": 5000,
"maxCorpusCount": -1,
"minDictionaryCount": 1,
"maxDictionaryCount": -1,
"limit": 1,
"api_key": api_key
}
async def start(self, ctx, letters: int):
if self._test_document(str(ctx.channel_id)):
await ctx.send("There is already a wordle game in this channel.")
self.bot.log("There was already a game going on")
return
async def start(self, ctx, letters: int):
if self._test_document(str(ctx.channel_id)):
await ctx.send("There is already a wordle game in this channel.")
self.bot.log("There was already a game going on")
return
params = self._APIPARAMS
params["minLength"] = letters
params["maxLength"] = letters
word = "-"
while "-" in word or "." in word:
response = requests.get(self._API_url, params=params)
if response.json() == []:
ctx.send("Could not find a word. Try again")
return
word = list(response.json()[0]["word"].upper())
params = self._APIPARAMS
params["minLength"] = letters
params["maxLength"] = letters
word = "-"
while "-" in word or "." in word:
response = requests.get(self._API_url, params=params)
if response.json() == []:
ctx.send("Could not find a word. Try again")
return
word = list(response.json()[0]["word"].upper())
self.bot.log(f"Found the word \"{''.join(word)}\"")
game = {"word": word, "guesses": [], "results": []}
await ctx.send("Starting a wordle game.")
await self._start_new(ctx.channel, game, delete=False)
self.bot.log(f"Found the word \"{''.join(word)}\"")
game = {"word": word, "guesses": [], "results": []}
await ctx.send("Starting a wordle game.")
await self._start_new(ctx.channel, game, delete=False)
def _get_result(self, guess: list[str], word: list[str]):
result = ["_" for _ in guess]
for i, letter in enumerate(guess):
if letter == word[i]:
result[i] = "*"
def _get_result(self, guess: list[str], word: list[str]):
result = ["_" for _ in guess]
for i, letter in enumerate(guess):
if letter == word[i]:
result[i] = "*"
for i, letter in enumerate(guess):
if letter in word and result[i] != "*":
same_letter = guess[:i].count(letter)
same_letter += len(
[
l for j,l in
enumerate(guess[i:])
if guess[i:][j] == letter and result[i:][j] == "*"
]
)
if same_letter < word.count(letter):
result[i] = "-"
for i, letter in enumerate(guess):
if letter in word and result[i] != "*":
same_letter = guess[:i].count(letter)
same_letter += len(
[
l for j,l in
enumerate(guess[i:])
if guess[i:][j] == letter and result[i:][j] == "*"
]
)
if same_letter < word.count(letter):
result[i] = "-"
return result
return result
async def guess(self, ctx, guess: str):
if not guess.isalpha():
await ctx.send("You can only use letters in your guess")
return
async def guess(self, ctx, guess: str):
if not guess.isalpha():
await ctx.send("You can only use letters in your guess")
return
guess = list(guess.upper())
try:
game = self.access_document(str(ctx.channel_id))
except GameNotInDatabase:
await ctx.send("No game in channel")
return
guess = list(guess.upper())
try:
game = self.access_document(str(ctx.channel_id))
except GameNotInDatabase:
await ctx.send("No game in channel")
return
if len(guess) != len(game['word']):
await ctx.send(
f"Your guess must be {len(game['word'])} letters long")
return
if len(guess) != len(game['word']):
await ctx.send(
f"Your guess must be {len(game['word'])} letters long")
return
await ctx.send(f"Guessed {''.join(guess)}")
result = self._get_result(guess, game['word'])
await ctx.send(f"Guessed {''.join(guess)}")
result = self._get_result(guess, game['word'])
updater = {
"$set": {
f"guesses.{len(game['guesses'])}": guess,
f"results.{len(game['guesses'])}": result
}
}
self._update_document(str(ctx.channel_id), updater)
updater = {
"$set": {
f"guesses.{len(game['guesses'])}": guess,
f"results.{len(game['guesses'])}": result
}
}
self._update_document(str(ctx.channel_id), updater)
if result == ["*" for _ in game['word']]:
await ctx.send("You guessed the word! Adding 15 GwendoBucks to your account")
self.bot.money.addMoney(f"#{ctx.author_id}", 15)
await self._end_game(ctx.channel)
elif len(game['guesses']) == 5:
await ctx.send(f"You used up all available guesses. The word was '{''.join(game['word'])}'")
await self._end_game(ctx.channel)
else:
print(len(game['guesses']))
await self._delete_old_image(ctx.channel)
await self._send_image(ctx.channel, delete=False)
if result == ["*" for _ in game['word']]:
await ctx.send("You guessed the word! Adding 15 GwendoBucks to your account")
self.bot.money.addMoney(f"#{ctx.author_id}", 15)
await self._end_game(ctx.channel)
elif len(game['guesses']) == 5:
await ctx.send(f"You used up all available guesses. The word was '{''.join(game['word'])}'")
await self._end_game(ctx.channel)
else:
print(len(game['guesses']))
await self._delete_old_image(ctx.channel)
await self._send_image(ctx.channel, delete=False)
class WordleDrawer(BaseDrawer):
def __init__(self, bot, game: WordleGame):
super().__init__(bot, game)
self.default_image = None
self.default_color = BACKGROUND_COLOR
self.default_size = (0, 0)
def __init__(self, bot, game: WordleGame):
super().__init__(bot, game)
self.default_image = None
self.default_color = BACKGROUND_COLOR
self.default_size = (0, 0)
def _get_size(self, game: dict):
width = (
(len(game['word']) * SQUARE_SIZE) +
((len(game['word']) - 1) * SQUARE_PADDING) +
(2 * IMAGE_MARGIN)
)
height = (
(6 * SQUARE_SIZE) +
(5 * ROW_PADDING) +
(2 * IMAGE_MARGIN)
)
size = (width, height)
def _get_size(self, game: dict):
width = (
(len(game['word']) * SQUARE_SIZE) +
((len(game['word']) - 1) * SQUARE_PADDING) +
(2 * IMAGE_MARGIN)
)
height = (
(6 * SQUARE_SIZE) +
(5 * ROW_PADDING) +
(2 * IMAGE_MARGIN)
)
size = (width, height)
return size
return size
def _draw_row(self, row, drawer, font, word: str, colors: list[str] = None,
border_color: str = None):
if colors is None:
colors = [BACKGROUND_COLOR for _ in range(len(word))]
def _draw_row(self, row, drawer, font, word: str, colors: list[str] = None,
border_color: str = None):
if colors is None:
colors = [BACKGROUND_COLOR for _ in range(len(word))]
for i, letter in enumerate(word):
y_pos = IMAGE_MARGIN + (row * (SQUARE_SIZE + ROW_PADDING))
x_pos = IMAGE_MARGIN + (i * (SQUARE_SIZE + SQUARE_PADDING))
top_left = (x_pos, y_pos)
bottom_right = (x_pos + SQUARE_SIZE, y_pos + SQUARE_SIZE)
for i, letter in enumerate(word):
y_pos = IMAGE_MARGIN + (row * (SQUARE_SIZE + ROW_PADDING))
x_pos = IMAGE_MARGIN + (i * (SQUARE_SIZE + SQUARE_PADDING))
top_left = (x_pos, y_pos)
bottom_right = (x_pos + SQUARE_SIZE, y_pos + SQUARE_SIZE)
drawer.rounded_rectangle(
(top_left,bottom_right),
10,
colors[i],
border_color,
3
)
drawer.rounded_rectangle(
(top_left,bottom_right),
10,
colors[i],
border_color,
3
)
text_pos = (
x_pos + (SQUARE_SIZE//2),
y_pos + int(SQUARE_SIZE * 0.6)
)
text_pos = (
x_pos + (SQUARE_SIZE//2),
y_pos + int(SQUARE_SIZE * 0.6)
)
drawer.text(text_pos, letter, TEXT_COLOR, font=font, anchor="mm")
drawer.text(text_pos, letter, TEXT_COLOR, font=font, anchor="mm")
def _determine_colors(self, results):
return [COLORS[["_","-","*"].index(symbol)] for symbol in results]
def _determine_colors(self, results):
return [COLORS[["_","-","*"].index(symbol)] for symbol in results]
def _draw_image(self, game: dict, image: Image.Image):
drawer = ImageDraw.Draw(image)
font = self._get_font(FONT_SIZE)
for i, guess in enumerate(game['guesses']):
colors = self._determine_colors(game['results'][i])
self._draw_row(i, drawer, font, guess, colors)
def _draw_image(self, game: dict, image: Image.Image):
drawer = ImageDraw.Draw(image)
font = self._get_font(FONT_SIZE)
for i, guess in enumerate(game['guesses']):
colors = self._determine_colors(game['results'][i])
self._draw_row(i, drawer, font, guess, colors)
if len(game["guesses"]) < 6:
self._draw_row(
len(game['guesses']),
drawer,
font,
" "*len(game['word']),
border_color=WRITING_BORDER_COLOR
)
if len(game["guesses"]) < 6:
self._draw_row(
len(game['guesses']),
drawer,
font,
" "*len(game['word']),
border_color=WRITING_BORDER_COLOR
)
for i in range(5 - len(game['guesses'])):
self._draw_row(
len(game['guesses']) + i + 1,
drawer,
font, " "*len(game['word']),
border_color=INACTIVE_BORDER_COLOR
)
for i in range(5 - len(game['guesses'])):
self._draw_row(
len(game['guesses']) + i + 1,
drawer,
font, " "*len(game['word']),
border_color=INACTIVE_BORDER_COLOR
)
return super()._draw_image(game, image)
return super()._draw_image(game, image)