🧹 Cleaning up cogs

This commit is contained in:
NikolajDanger
2021-03-31 16:53:37 +02:00
parent 92fe7913f7
commit aff29a8d91
10 changed files with 166 additions and 126 deletions

View File

@ -4,7 +4,7 @@ from discord.ext import commands
from discord_slash import SlashCommand from discord_slash import SlashCommand
from pymongo import MongoClient from pymongo import MongoClient
from funcs import Money, StarWars, Games, Other, LookupFuncs from funcs import Money, StarWars, Games, Other, LookupFuncs
from utils import Options, Credentials, logThis, makeFiles, databaseFuncs from utils import Options, Credentials, logThis, makeFiles, databaseFuncs, EventHandler, ErrorHandler
class Gwendolyn(commands.Bot): class Gwendolyn(commands.Bot):
def __init__(self): def __init__(self):
@ -25,6 +25,8 @@ class Gwendolyn(commands.Bot):
self.games = Games(self) self.games = Games(self)
self.money = Money(self) self.money = Money(self)
self.databaseFuncs = databaseFuncs(self) self.databaseFuncs = databaseFuncs(self)
self.eventHandler = EventHandler(self)
self.errorHandler = ErrorHandler(self)
intents = discord.Intents.default() intents = discord.Intents.default()
intents.members = True intents.members = True

View File

@ -1,4 +1,4 @@
import discord, traceback import discord
from discord.ext import commands from discord.ext import commands
class EventCog(commands.Cog): class EventCog(commands.Cog):
@ -8,10 +8,7 @@ class EventCog(commands.Cog):
# Syncs commands, sets the game, and logs when the bot logs in # Syncs commands, sets the game, and logs when the bot logs in
@commands.Cog.listener() @commands.Cog.listener()
async def on_ready(self): async def on_ready(self):
await self.bot.databaseFuncs.syncCommands() await self.bot.eventHandler.on_ready()
self.bot.log("Logged in as "+self.bot.user.name+", "+str(self.bot.user.id), level = 25)
game = discord.Game("Use /help for commands")
await self.bot.change_presence(activity=game, status = discord.Status.online)
@commands.Cog.listener() @commands.Cog.listener()
async def on_disconnect(self): async def on_disconnect(self):
@ -20,38 +17,18 @@ class EventCog(commands.Cog):
# Logs when user sends a command # Logs when user sends a command
@commands.Cog.listener() @commands.Cog.listener()
async def on_slash_command(self, ctx): async def on_slash_command(self, ctx):
self.bot.log(f"{ctx.author.display_name} ran /{ctx.name}", str(ctx.channel_id), level = 25) logMessage = f"{ctx.author.display_name} ran /{ctx.name}"
self.bot.log(logMessage, str(ctx.channel_id), level = 25)
# Logs if a command experiences an error # Logs if a command experiences an error
@commands.Cog.listener() @commands.Cog.listener()
async def on_slash_command_error(self, ctx, error): async def on_slash_command_error(self, ctx, error):
if isinstance(error, commands.CommandNotFound): await self.bot.errorHandler.on_slash_command_error(ctx, error)
await ctx.send("That's not a command (error code 001)")
elif isinstance(error,commands.errors.MissingRequiredArgument):
self.bot.log(f"{error}",str(ctx.channel_id))
await ctx.send("Missing command parameters (error code 002). Try using `!help [command]` to find out how to use the command.")
else:
exception = traceback.format_exception(type(error), error, error.__traceback__)
stopAt = "\nThe above exception was the direct cause of the following exception:\n\n"
if stopAt in exception:
index = exception.index(stopAt)
exception = exception[:index]
exceptionString = "".join(exception)
self.bot.log([f"exception in /{ctx.name}", f"{exceptionString}"],str(ctx.channel_id), 40)
await ctx.send("Something went wrong (error code 000)")
# Logs if an error occurs # Logs if an error occurs
@commands.Cog.listener() @commands.Cog.listener()
async def on_error(self, method): async def on_error(self, method):
exception = traceback.format_exc() await self.bot.errorHandler.on_error(method)
stopAt = "\nThe above exception was the direct cause of the following exception:\n\n"
if stopAt in exception:
index = exception.index(stopAt)
exception = exception[:index]
exceptionString = "".join(exception)
self.bot.log([f"exception in /{method}", f"{exceptionString}"], level = 40)
def setup(bot): def setup(bot):
bot.add_cog(EventCog(bot)) bot.add_cog(EventCog(bot))

View File

@ -1,8 +1,6 @@
import discord, asyncio, json import discord, asyncio, json
from discord.ext import commands from discord.ext import commands
from discord_slash import cog_ext from discord_slash import cog_ext
from discord_slash import SlashCommandOptionType as scot
from utils import getParams from utils import getParams
params = getParams() params = getParams()
@ -15,73 +13,22 @@ class GamesCog(commands.Cog):
# Checks user balance # Checks user balance
@cog_ext.cog_slash(**params["balance"]) @cog_ext.cog_slash(**params["balance"])
async def balance(self, ctx): async def balance(self, ctx):
await ctx.defer() await self.bot.money.sendBalance(ctx)
response = self.bot.money.checkBalance("#"+str(ctx.author.id))
if response == 1:
new_message = ctx.author.display_name + " has " + str(response) + " GwendoBuck"
else:
new_message = ctx.author.display_name + " has " + str(response) + " GwendoBucks"
await ctx.send(new_message)
# Gives another user an amount of GwendoBucks # Gives another user an amount of GwendoBucks
@cog_ext.cog_slash(**params["give"]) @cog_ext.cog_slash(**params["give"])
async def give(self, ctx, user, amount): async def give(self, ctx, user, amount):
await ctx.defer() await self.bot.money.giveMoney(ctx, user, amount)
username = user.display_name
if self.bot.databaseFuncs.getID(username) == None:
async for member in ctx.guild.fetch_members(limit=None):
if member.display_name.lower() == username.lower():
username = member.display_name
userID = "#" + str(member.id)
self.bot.database["users"].insert_one({"_id":userID,"user name":username,"money":0})
response = self.bot.money.giveMoney("#"+str(ctx.author.id),username,amount)
await ctx.send(response)
# Invest GwendoBucks in the stock market # Invest GwendoBucks in the stock market
@cog_ext.cog_slash(**params["invest"]) @cog_ext.cog_slash(**params["invest"])
async def invest(self, ctx, parameters = "check"): async def invest(self, ctx, parameters = "check"):
await ctx.defer() await self.bot.games.invest.parseInvest(ctx, parameters)
response = self.bot.games.invest.parseInvest(parameters,"#"+str(ctx.author.id))
if response.startswith("**"):
responses = response.split("\n")
em = discord.Embed(title=responses[0],description="\n".join(responses[1:]),colour=0x00FF00)
await ctx.send(embed=em)
else:
await ctx.send(response)
# Runs a game of trivia # Runs a game of trivia
@cog_ext.cog_slash(**params["trivia"]) @cog_ext.cog_slash(**params["trivia"])
async def trivia(self, ctx, answer = ""): async def trivia(self, ctx, answer = ""):
await ctx.defer() await self.bot.games.trivia.triviaParse(ctx, answer)
if answer == "":
question, options, correctAnswer = self.bot.games.trivia.triviaStart(str(ctx.channel_id))
if options != "":
results = "**"+question+"**\n"
for x, option in enumerate(options):
results += chr(x+97) + ") "+option+"\n"
await ctx.send(results)
await asyncio.sleep(60)
self.bot.games.trivia.triviaCountPoints(str(ctx.channel_id))
self.bot.databaseFuncs.deleteGame("trivia questions",str(ctx.channel_id))
self.bot.log("Time's up for the trivia question",str(ctx.channel_id))
await ctx.send("Time's up The answer was \""+chr(correctAnswer)+") "+options[correctAnswer-97]+"\". Anyone who answered that has gotten 1 GwendoBuck")
else:
await ctx.send(question, hidden=True)
elif answer in ["a","b","c","d"]:
response = self.bot.games.trivia.triviaAnswer("#"+str(ctx.author.id),str(ctx.channel_id),answer)
if response.startswith("Locked in "):
await ctx.send(f"{ctx.author.display_name} answered {answer}")
else:
await ctx.send(response)
else:
self.bot.log("I didn't understand that (error code 1101)",str(ctx.channel_id))
await ctx.send("I didn't understand that (error code 1101)")
class BlackjackCog(commands.Cog): class BlackjackCog(commands.Cog):

View File

@ -170,7 +170,6 @@ class Blackjack():
return hand, allStanding, preAllStanding return hand, allStanding, preAllStanding
# When players try to hit # When players try to hit
def blackjackHit(self,channel,user,handNumber = 0): def blackjackHit(self,channel,user,handNumber = 0):
game = self.bot.database["blackjack games"].find_one({"_id":channel}) game = self.bot.database["blackjack games"].find_one({"_id":channel})
@ -224,7 +223,6 @@ class Blackjack():
self.bot.log(user+" tried to hit without being in the game") self.bot.log(user+" tried to hit without being in the game")
return "You have to enter the game before you can hit" return "You have to enter the game before you can hit"
# When players try to double down # When players try to double down
def blackjackDouble(self,channel,user,handNumber = 0): def blackjackDouble(self,channel,user,handNumber = 0):
game = self.bot.database["blackjack games"].find_one({"_id":channel}) game = self.bot.database["blackjack games"].find_one({"_id":channel})
@ -562,7 +560,6 @@ class Blackjack():
return finalWinnings return finalWinnings
def calcWinnings(self,hand, dealerValue, topLevel, dealerBlackjack, dealerBusted): def calcWinnings(self,hand, dealerValue, topLevel, dealerBlackjack, dealerBusted):
self.bot.log("Calculating winnings") self.bot.log("Calculating winnings")
reason = "" reason = ""

View File

@ -1,3 +1,5 @@
import discord
class Invest(): class Invest():
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
@ -103,35 +105,48 @@ class Invest():
else: else:
return "no" return "no"
def parseInvest(self, content: str, user : str): async def parseInvest(self, ctx, parameters):
if content.startswith("check"): try:
commands = content.split(" ") await ctx.defer()
except:
self.bot.log("Defer failed")
user = f"#{ctx.author.id}"
if parameters.startswith("check"):
commands = parameters.split(" ")
if len(commands) == 1: if len(commands) == 1:
return self.getPortfolio(user) response = self.getPortfolio(user)
else: else:
price = self.getPrice(commands[1]) price = self.getPrice(commands[1])
if price == 0: if price == 0:
return f"{commands[1].upper()} is not traded on the american market." response = f"{commands[1].upper()} is not traded on the american market."
else: else:
price = f"{price:,}".replace(",",".") price = f"{price:,}".replace(",",".")
return f"The current {commands[1].upper()} stock is valued at **{price}** GwendoBucks" response = f"The current {commands[1].upper()} stock is valued at **{price}** GwendoBucks"
elif content.startswith("buy"): elif parameters.startswith("buy"):
commands = content.split(" ") commands = parameters.split(" ")
if len(commands) == 3: if len(commands) == 3:
#try: response = self.buyStock(user,commands[1],int(commands[2]))
return self.buyStock(user,commands[1],int(commands[2]))
#except:
# return "The command must be given as \"/invest buy [stock] [amount of GwendoBucks to purchase with]\""
else: else:
return "You must give both a stock name and an amount of gwendobucks you wish to spend." response = "You must give both a stock name and an amount of gwendobucks you wish to spend."
elif content.startswith("sell"): elif parameters.startswith("sell"):
commands = content.split(" ") commands = parameters.split(" ")
if len(commands) == 3: if len(commands) == 3:
try: try:
return self.sellStock(user,commands[1],int(commands[2])) response = self.sellStock(user,commands[1],int(commands[2]))
except: except:
return "The command must be given as \"/invest sell [stock] [amount of GwendoBucks to sell stocks for]\"" response = "The command must be given as \"/invest sell [stock] [amount of GwendoBucks to sell stocks for]\""
else: else:
return "You must give both a stock name and an amount of GwendoBucks you wish to sell stocks for." response = "You must give both a stock name and an amount of GwendoBucks you wish to sell stocks for."
else:
response = "Incorrect parameters"
if response.startswith("**"):
responses = response.split("\n")
em = discord.Embed(title=responses[0],description="\n".join(responses[1:]),colour=0x00FF00)
await ctx.send(embed=em)
else:
await ctx.send(response)

View File

@ -14,6 +14,15 @@ class Money():
return userData["money"] return userData["money"]
else: return 0 else: return 0
async def sendBalance(self, ctx):
await ctx.defer()
response = self.checkBalance("#"+str(ctx.author.id))
if response == 1:
new_message = ctx.author.display_name + " has " + str(response) + " GwendoBuck"
else:
new_message = ctx.author.display_name + " has " + str(response) + " GwendoBucks"
await ctx.send(new_message)
# Adds money to the account of a user # Adds money to the account of a user
def addMoney(self,user,amount): def addMoney(self,user,amount):
self.bot.log("adding "+str(amount)+" to "+user+"'s account") self.bot.log("adding "+str(amount)+" to "+user+"'s account")
@ -26,26 +35,39 @@ class Money():
self.database["users"].insert_one({"_id":user,"user name":self.bot.databaseFuncs.getName(user),"money":amount}) self.database["users"].insert_one({"_id":user,"user name":self.bot.databaseFuncs.getName(user),"money":amount})
# Transfers money from one user to another # Transfers money from one user to another
def giveMoney(self,user,targetUser,amount): async def giveMoney(self, ctx, user, amount):
userData = self.database["users"].find_one({"_id":user}) try:
targetUser = self.bot.databaseFuncs.getID(targetUser) await ctx.defer()
except:
self.bot.log("Defer failed")
username = user.display_name
if self.bot.databaseFuncs.getID(username) == None:
async for member in ctx.guild.fetch_members(limit=None):
if member.display_name.lower() == username.lower():
username = member.display_name
userID = "#" + str(member.id)
newUser = {"_id":userID,"user name":username,"money":0}
self.bot.database["users"].insert_one(newUser)
userData = self.database["users"].find_one({"_id":f"#{ctx.author.id}"})
targetUser = self.bot.databaseFuncs.getID(username)
if amount > 0: if amount > 0:
if targetUser != None: if targetUser != None:
if userData != None: if userData != None:
if userData["money"] >= amount: if userData["money"] >= amount:
self.addMoney(user,-1 * amount) self.addMoney(f"#{ctx.author.id}",-1 * amount)
self.addMoney(targetUser,amount) self.addMoney(targetUser,amount)
return "Transferred "+str(amount)+" GwendoBucks to "+self.bot.databaseFuncs.getName(targetUser) await ctx.send(f"Transferred {amount} GwendoBucks to {username}")
else: else:
self.bot.log("They didn't have enough GwendoBucks (error code 1223b)") self.bot.log("They didn't have enough GwendoBucks")
return "You don't have that many GwendoBucks (error code 1223b)" await ctx.send("You don't have that many GwendoBucks")
else: else:
self.bot.log("They didn't have enough GwendoBucks (error code 1223a)") self.bot.log("They didn't have enough GwendoBucks")
return "You don't have that many GwendoBucks (error code 1223a)" await ctx.send("You don't have that many GwendoBuck")
else: else:
self.bot.log("They weren't in the system") self.bot.log("They weren't in the system")
return "The target doesn't exist" await ctx.send("The target doesn't exist")
else: else:
self.bot.log("They tried to steal") self.bot.log("They tried to steal")
return "Yeah, no. You can't do that" await ctx.send("Yeah, no. You can't do that")

View File

@ -1,6 +1,7 @@
import json import json
import urllib import urllib
import random import random
import asyncio
class Trivia(): class Trivia():
def __init__(self, bot): def __init__(self, bot):
@ -83,3 +84,38 @@ class Trivia():
self.bot.log("Couldn't find the question (error code 1102)") self.bot.log("Couldn't find the question (error code 1102)")
return None return None
async def triviaParse(self, ctx, answer):
try:
await ctx.defer()
except:
self.bot.log("defer failed")
if answer == "":
question, options, correctAnswer = self.triviaStart(str(ctx.channel_id))
if options != "":
results = "**"+question+"**\n"
for x, option in enumerate(options):
results += chr(x+97) + ") "+option+"\n"
await ctx.send(results)
await asyncio.sleep(60)
self.triviaCountPoints(str(ctx.channel_id))
self.bot.databaseFuncs.deleteGame("trivia questions",str(ctx.channel_id))
self.bot.log("Time's up for the trivia question",str(ctx.channel_id))
await ctx.send("Time's up The answer was \""+chr(correctAnswer)+") "+options[correctAnswer-97]+"\". Anyone who answered that has gotten 1 GwendoBuck")
else:
await ctx.send(question, hidden=True)
elif answer in ["a","b","c","d"]:
response = self.triviaAnswer("#"+str(ctx.author.id), str(ctx.channel_id), answer)
if response.startswith("Locked in "):
await ctx.send(f"{ctx.author.display_name} answered {answer})")
else:
await ctx.send(response)
else:
self.bot.log("I didn't understand that (error code 1101)",str(ctx.channel_id))
await ctx.send("I didn't understand that (error code 1101)")

View File

@ -84,7 +84,7 @@ All the Python code should follow the [PEP 8 guidelines](https://www.python.org/
Code called by a command should not have `try` and `except` statements. All errors should be raised to the `on_command_error()` or `Command.error()` functions, where they can be dealt with. Code called by a command should not have `try` and `except` statements. All errors should be raised to the `on_command_error()` or `Command.error()` functions, where they can be dealt with.
## Cogs ## Cogs
The `Command` methods in cogs should only exist to perform small tasks or call code from elsewhere in the Gwendolyn code. Therefore, a single `Command` method should not contain more than 3 lines of code and should not use any modules other than `Discord.py` and the Gwendolyn modules. The `Command` methods in cogs should only exist to perform small tasks or call code from elsewhere in the Gwendolyn code. Therefore, a single `Command` method should not contain more than 3 lines of code and should not use any modules other than `Discord.py` and the Gwendolyn modules. If a cog method calls a function in Gwendolyn, ctx must be passed, and the function should handle sending messages.
## Codebase Management ## Codebase Management
### Folders ### Folders
@ -102,11 +102,11 @@ Things you should know about the logging:
+ The function can take either a list of strings or a string as its first parameter. If the parameter is a string, it is converted to a list of 1 string. + The function can take either a list of strings or a string as its first parameter. If the parameter is a string, it is converted to a list of 1 string.
+ The first string in the list is printed. All strings in the list are logged to the log-file. + The first string in the list is printed. All strings in the list are logged to the log-file.
+ If the list is longer than 1 string, `(details in log)` is added to the printed string. + If the list is longer than 1 string, `(details in log)` is added to the printed string.
+ The level parameter is 20 by default, which means the level is `INFO`. 40 corresponds to a level of `ERROR`, and 10 corresponds to a level of `DEBUG`. Only use these levels when logging. + The level parameter is 20 by default, which means the level is `INFO`. 40 corresponds to a level of `ERROR`, and 25 corresponds to `print`.
+ Logs of level `DEBUG` are not printed. + Logs of level `INFO` are not printed.
+ Logs of level `ERROR` should only be created in the `on_command_error()` or `Command.error()` functions. + Logs of level `ERROR` should only be created in the `on_command_error()`, `on_error()` or `Command.error()` functions.
### Logging rules ### Logging rules
1. Never call the `logThis()` function from `/utils/utilFuncs/`. Always call `bot.log`. 1. Never call the `logThis()` function from `/utils/utilFuncs/`. Always call `bot.log`.
1. The `on_slash_command()` and `on_ready()` events are the only times log should be called at level 20.`DEBUG` level logs should be used for all other logging. 1. The `on_slash_command()` and `on_ready()` events are the only times log should be called at level 25. `INFO` level logs should be used for all other logging.
1. Always provide the channel id if available. Although you shouldn't pass the channel id to a function purely to use it in logs. 1. Always provide the channel id if available. Although you shouldn't pass the channel id to a function purely to use it in logs.

View File

@ -1,6 +1,7 @@
"""A collections of utilities used by Gwendolyn and her functions""" """A collections of utilities used by Gwendolyn and her functions"""
__all__ = ["Options", "Credentials", "databaseFuncs", "getParams", "logThis", "cap", "makeFiles", "replaceMultiple", "emojiToCommand"] __all__ = ["Options", "Credentials", "databaseFuncs", "EventHandler", "ErrorHandler", "getParams", "logThis", "cap", "makeFiles", "replaceMultiple", "emojiToCommand"]
from .helperClasses import Options, Credentials, databaseFuncs from .helperClasses import Options, Credentials, databaseFuncs
from .eventHandlers import EventHandler, ErrorHandler
from .utilFunctions import getParams, logThis, cap, makeFiles, replaceMultiple, emojiToCommand from .utilFunctions import getParams, logThis, cap, makeFiles, replaceMultiple, emojiToCommand

43
utils/eventHandlers.py Normal file
View File

@ -0,0 +1,43 @@
import discord, traceback
from discord.ext import commands
class EventHandler():
def __init__(self, bot):
self.bot = bot
async def on_ready(self):
await self.bot.databaseFuncs.syncCommands()
self.bot.log("Logged in as "+self.bot.user.name+", "+str(self.bot.user.id), level = 25)
game = discord.Game("Use /help for commands")
await self.bot.change_presence(activity=game, status = discord.Status.online)
class ErrorHandler():
def __init__(self, bot):
self.bot = bot
async def on_slash_command_error(self, ctx, error):
if isinstance(error, commands.CommandNotFound):
await ctx.send("That's not a command (error code 001)")
elif isinstance(error,commands.errors.MissingRequiredArgument):
self.bot.log(f"{error}",str(ctx.channel_id))
await ctx.send("Missing command parameters (error code 002). Try using `!help [command]` to find out how to use the command.")
else:
exception = traceback.format_exception(type(error), error, error.__traceback__)
stopAt = "\nThe above exception was the direct cause of the following exception:\n\n"
if stopAt in exception:
index = exception.index(stopAt)
exception = exception[:index]
exceptionString = "".join(exception)
self.bot.log([f"exception in /{ctx.name}", f"{exceptionString}"],str(ctx.channel_id), 40)
await ctx.send("Something went wrong (error code 000)")
async def on_error(self, method):
exception = traceback.format_exc()
stopAt = "\nThe above exception was the direct cause of the following exception:\n\n"
if stopAt in exception:
index = exception.index(stopAt)
exception = exception[:index]
exceptionString = "".join(exception)
self.bot.log([f"exception in /{method}", f"{exceptionString}"], level = 40)