Merge pull request #64 from NikolajDanger/improve_codebase

Improve cogs and better slash command integration
This commit is contained in:
NikolajDanger
2021-04-06 15:16:00 +02:00
committed by GitHub
33 changed files with 1668 additions and 1546 deletions

View File

@ -4,7 +4,7 @@ from discord.ext import commands
from discord_slash import SlashCommand
from pymongo import MongoClient
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):
def __init__(self):
@ -25,6 +25,8 @@ class Gwendolyn(commands.Bot):
self.games = Games(self)
self.money = Money(self)
self.databaseFuncs = databaseFuncs(self)
self.eventHandler = EventHandler(self)
self.errorHandler = ErrorHandler(self)
intents = discord.Intents.default()
intents.members = True
@ -34,6 +36,20 @@ class Gwendolyn(commands.Bot):
def log(self, messages, channel : str = "", level : int = 20):
logThis(messages, channel, level)
async def stop(self, ctx):
if f"#{ctx.author.id}" in self.options.admins:
await ctx.send("Pulling git repo and restarting...")
await self.change_presence(status = discord.Status.offline)
self.databaseFuncs.stopServer()
self.log("Logging out", level = 25)
await self.close()
else:
self.log(f"{ctx.author.display_name} tried to stop me! (error code 201)",str(ctx.channel_id))
await ctx.send(f"I don't think I will, {ctx.author.display_name} (error code 201)")
if __name__ == "__main__":

View File

@ -1,57 +1,34 @@
import discord, traceback
from discord.ext import commands
class EventCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.bot.on_error = self.on_error
# Syncs commands, sets the game, and logs when the bot logs in
@commands.Cog.listener()
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)
@commands.Cog.listener()
async def on_disconnect(self):
await self.bot.change_presence(status = discord.Status.offline)
await self.bot.eventHandler.on_ready()
# Logs when user sends a command
@commands.Cog.listener()
async def on_slash_command(self, ctx):
self.bot.log(f"{ctx.author.display_name} ran /{ctx.name}", str(ctx.channel_id), level = 25)
await self.bot.eventHandler.on_slash_command(ctx)
# Logs if a command experiences an error
@commands.Cog.listener()
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]
await self.bot.errorHandler.on_slash_command_error(ctx, error)
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 on error occurs
async def on_error(self, method, *args, **kwargs):
await self.bot.errorHandler.on_error(method)
# Logs if an error occurs
# If someone reacted to a message, checks if it's a reaction it's
# Gwendolyn has been waiting for, and then does something
@commands.Cog.listener()
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)
async def on_reaction_add(self, reaction, user):
await self.bot.eventHandler.on_reaction_add(reaction, user)
def setup(bot):
bot.add_cog(EventCog(bot))

View File

@ -1,7 +1,5 @@
import discord, asyncio, json
from discord.ext import commands
from discord_slash import cog_ext
from discord_slash import SlashCommandOptionType as scot
from utils import getParams
@ -15,73 +13,22 @@ class GamesCog(commands.Cog):
# Checks user balance
@cog_ext.cog_slash(**params["balance"])
async def balance(self, ctx):
await ctx.defer()
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)
await self.bot.money.sendBalance(ctx)
# Gives another user an amount of GwendoBucks
@cog_ext.cog_slash(**params["give"])
async def give(self, ctx, user, amount):
await ctx.defer()
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)
await self.bot.money.giveMoney(ctx, user, amount)
# Invest GwendoBucks in the stock market
@cog_ext.cog_slash(**params["invest"])
async def invest(self, ctx, parameters = "check"):
await ctx.defer()
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)
await self.bot.games.invest.parseInvest(ctx, parameters)
# Runs a game of trivia
@cog_ext.cog_slash(**params["trivia"])
async def trivia(self, ctx, answer = ""):
await ctx.defer()
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)")
await self.bot.games.trivia.triviaParse(ctx, answer)
class BlackjackCog(commands.Cog):
@ -92,103 +39,113 @@ class BlackjackCog(commands.Cog):
# Starts a game of blackjack
@cog_ext.cog_subcommand(**params["blackjackStart"])
async def blackjackStart(self, ctx):
await ctx.defer()
await self.bot.games.blackjack.parseBlackjack("", ctx)
await self.bot.games.blackjack.start(ctx)
@cog_ext.cog_subcommand(**params["blackjackBet"])
async def blackjackBet(self, ctx, bet):
await ctx.defer()
await self.bot.games.blackjack.parseBlackjack(f"bet {bet}", ctx)
await self.bot.games.blackjack.playerDrawHand(ctx, bet)
@cog_ext.cog_subcommand(**params["blackjackStand"])
async def blackjackStand(self, ctx, hand = ""):
await ctx.defer()
await self.bot.games.blackjack.parseBlackjack(f"stand {hand}", ctx)
await self.bot.games.blackjack.stand(ctx, hand)
@cog_ext.cog_subcommand(**params["blackjackHit"])
async def blackjackHit(self, ctx, hand = ""):
await ctx.defer()
await self.bot.games.blackjack.parseBlackjack(f"hit {hand}", ctx)
async def blackjackHit(self, ctx, hand = 0):
await self.bot.games.blackjack.hit(ctx, hand)
@cog_ext.cog_subcommand(**params["blackjackDouble"])
async def blackjackDouble(self, ctx, hand = 0):
await self.bot.games.blackjack.double(ctx, hand)
@cog_ext.cog_subcommand(**params["blackjackSplit"])
async def blackjackSplit(self, ctx, hand = 0):
await self.bot.games.blackjack.split(ctx, hand)
@cog_ext.cog_subcommand(**params["blackjackHilo"])
async def blackjackHilo(self, ctx):
await self.bot.games.blackjack.hilo(ctx)
@cog_ext.cog_subcommand(**params["blackjackShuffle"])
async def blackjackShuffle(self, ctx):
await self.bot.games.blackjack.shuffle(ctx)
@cog_ext.cog_subcommand(**params["blackjackCards"])
async def blackjackCards(self, ctx):
await self.bot.games.blackjack.cards(ctx)
class ConnectFourCog(commands.Cog):
def __init__(self,bot):
"""Runs game stuff."""
self.bot = bot
# Start a game of connect four against a user
@cog_ext.cog_subcommand(**params["connectFourStartUser"])
async def connectFourStartUser(self, ctx, user):
await ctx.defer()
await self.bot.games.gameLoops.connectFour(ctx, "start "+user.display_name)
await self.bot.games.connectFour.start(ctx, user)
# Start a game of connect four against gwendolyn
@cog_ext.cog_subcommand(**params["connectFourStartGwendolyn"])
async def connectFourStartGwendolyn(self, ctx, difficulty = 3):
await ctx.defer()
await self.bot.games.gameLoops.connectFour(ctx, "start "+str(difficulty))
await self.bot.games.connectFour.start(ctx, difficulty)
# Stop the current game of connect four
@cog_ext.cog_subcommand(**params["connectFourStop"])
async def connectFourStop(self, ctx):
await self.bot.games.gameLoops.connectFour(ctx, "stop")
# Place a piece in the current game of connect four
@cog_ext.cog_subcommand(**params["connectFourPlace"])
async def connectFourPlace(self, ctx, column):
await self.bot.games.gameLoops.connectFour(ctx, "place "+str(column))
@cog_ext.cog_subcommand(**params["connectFourSurrender"])
async def connectFourSurrender(self, ctx):
await self.bot.games.connectFour.surrender(ctx)
class HangmanCog(commands.Cog):
def __init__(self,bot):
"""Runs game stuff."""
self.bot = bot
# Starts a game of Hangman
@cog_ext.cog_subcommand(**params["hangmanStart"])
async def hangmanStart(self, ctx):
await ctx.defer()
await self.bot.games.gameLoops.runHangman(ctx.channel,"#"+str(ctx.author.id),"start", ctx)
await self.bot.games.hangman.start(ctx)
# Stops a game of Hangman
@cog_ext.cog_subcommand(**params["hangmanStop"])
async def hangmanStop(self, ctx):
await self.bot.games.gameLoops.runHangman(ctx.channel,"#"+str(ctx.author.id),"stop", ctx)
await self.bot.games.hangman.stop(ctx)
class HexCog(commands.Cog):
def __init__(self,bot):
"""Runs game stuff."""
self.bot = bot
# Start a game of Hex against another user
@cog_ext.cog_subcommand(**params["hexStartUser"])
async def hexStartUser(self, ctx, user):
await ctx.defer()
await self.bot.games.gameLoops.runHex(ctx, "start "+user.display_name, "#"+str(ctx.author.id))
await self.bot.games.hex.start(ctx, user)
# Start a game of Hex against Gwendolyn
@cog_ext.cog_subcommand(**params["hexStartGwendolyn"])
async def hexStartGwendolyn(self, ctx, difficulty = 2):
await ctx.defer()
await self.bot.games.gameLoops.runHex(ctx, "start "+str(difficulty), "#"+str(ctx.author.id))
# Undo your last hex move
@cog_ext.cog_subcommand(**params["hexUndo"])
async def hexUndo(self, ctx):
await self.bot.games.gameLoops.runHex(ctx, "undo", "#"+str(ctx.author.id))
# Perform a hex swap
@cog_ext.cog_subcommand(**params["hexSwap"])
async def hexSwap(self, ctx):
await self.bot.games.gameLoops.runHex(ctx, "swap", "#"+str(ctx.author.id))
# Surrender the hex game
@cog_ext.cog_subcommand(**params["hexSurrender"])
async def hexSurrender(self, ctx):
await self.bot.games.gameLoops.runHex(ctx, "surrender", "#"+str(ctx.author.id))
await self.bot.games.hex.start(ctx, difficulty)
# Place a piece in the hex game
@cog_ext.cog_subcommand(**params["hexPlace"])
async def hexPlace(self, ctx, coordinates):
await self.bot.games.gameLoops.runHex(ctx, "place "+coordinates, "#"+str(ctx.author.id))
await self.bot.games.hex.placeHex(ctx, coordinates, f"#{ctx.author.id}")
# Undo your last hex move
@cog_ext.cog_subcommand(**params["hexUndo"])
async def hexUndo(self, ctx):
await self.bot.games.hex.undo(ctx)
# Perform a hex swap
@cog_ext.cog_subcommand(**params["hexSwap"])
async def hexSwap(self, ctx):
await self.bot.games.hex.swap(ctx)
# Surrender the hex game
@cog_ext.cog_subcommand(**params["hexSurrender"])
async def hexSurrender(self, ctx):
await self.bot.games.hex.surrender(ctx)
def setup(bot):
bot.add_cog(GamesCog(bot))

View File

@ -1,9 +1,7 @@
import discord, json
from discord.ext import commands
from discord_slash import cog_ext
from discord_slash import SlashCommandOptionType as scot
from utils import getParams, cap
from utils import getParams
params = getParams()
@ -15,58 +13,12 @@ class LookupCog(commands.Cog):
# Looks up a spell
@cog_ext.cog_slash(**params["spell"])
async def spell(self, ctx, query):
spell = self.bot.lookupFuncs.spellFunc(cap(query))
if len(spell) > 2000:
await ctx.send(spell[:2000])
await ctx.send(spell[2000:])
else:
await ctx.send(spell)
await self.bot.lookupFuncs.spellFunc(ctx, query)
# Looks up a monster
@cog_ext.cog_slash(**params["monster"])
async def monster(self, ctx, query):
title, text1, text2, text3, text4, text5 = self.bot.lookupFuncs.monsterFunc(cap(query))
em1 = discord.Embed(title = title, description = text1, colour=0xDEADBF)
# Sends the received information. Separates into separate messages if
# there is too much text
await ctx.send(embed = em1)
if text2 != "":
if len(text2) < 2048:
em2 = discord.Embed(title = "Special Abilities", description = text2, colour=0xDEADBF)
await ctx.send(embed = em2)
else:
em2 = discord.Embed(title = "Special Abilities", description = text2[:2048], colour=0xDEADBF)
await ctx.send(embed = em2)
em2_2 = discord.Embed(title = "", description = text2[2048:], colour=0xDEADBF)
await ctx.send(embed = em2_2)
if text3 != "":
if len(text3) < 2048:
em3 = discord.Embed(title = "Actions", description = text3, colour=0xDEADBF)
await ctx.send(embed = em3)
else:
em3 = discord.Embed(title = "Actions", description = text3[:2048], colour=0xDEADBF)
await ctx.send(embed = em3)
em3_2 = discord.Embed(title = "", description = text3[2048:], colour=0xDEADBF)
await ctx.send(embed = em3_2)
if text4 != "":
if len(text4) < 2048:
em4 = discord.Embed(title = "Reactions", description = text4, colour=0xDEADBF)
await ctx.send(embed = em4)
else:
em4 = discord.Embed(title = "Reactions", description = text4[:2048], colour=0xDEADBF)
await ctx.send(embed = em4)
em4_2 = discord.Embed(title = "", description = text4[2048:], colour=0xDEADBF)
await ctx.send(embed = em4_2)
if text5 != "":
if len(text5) < 2048:
em5 = discord.Embed(title = "Legendary Actions", description = text5, colour=0xDEADBF)
await ctx.send(embed = em5)
else:
em5 = discord.Embed(title = "Legendary Actions", description = text5[:2048], colour=0xDEADBF)
await ctx.send(embed = em5)
em5_2 = discord.Embed(title = "", description = text5[2048:], colour=0xDEADBF)
await ctx.send(embed = em5_2)
await self.bot.lookupFuncs.monsterFunc(ctx, query)
def setup(bot):
bot.add_cog(LookupCog(bot))

View File

@ -2,16 +2,9 @@ import discord, codecs, string, json
from discord.ext import commands
from discord_slash import cog_ext
from utils import Options
from utils import getParams
with open("resources/slashParameters.json", "r") as f:
params = json.load(f)
options = Options()
if options.testing:
for p in params:
params[p]["guild_ids"] = options.guildIds
params = getParams()
class MiscCog(commands.Cog):
def __init__(self, bot):
@ -30,31 +23,12 @@ class MiscCog(commands.Cog):
# Restarts the bot
@cog_ext.cog_slash(**params["stop"])
async def stop(self, ctx):
if "#"+str(ctx.author.id) in self.bot.options.admins:
await ctx.send("Pulling git repo and restarting...")
self.bot.databaseFuncs.stopServer()
self.bot.log("Logging out.")
await self.bot.logout()
else:
self.bot.log(f"{ctx.author.display_name} tried to stop me! (error code 201)",str(ctx.channel_id))
await ctx.send(f"I don't think I will, {ctx.author.display_name} (error code 201)")
await self.bot.stop(ctx)
# Gets help for specific command
@cog_ext.cog_slash(**params["help"])
async def helpCommand(self, ctx, command = ""):
if command == "":
with codecs.open("resources/help/help.txt",encoding="utf-8") as f:
text = f.read()
em = discord.Embed(title = "Help", description = text,colour = 0x59f442)
await ctx.send(embed = em)
else:
self.bot.log(f"Looking for help-{command}.txt",str(ctx.channel_id))
with codecs.open(f"resources/help/help-{command}.txt",encoding="utf-8") as f:
text = f.read()
em = discord.Embed(title = command.capitalize(), description = text,colour = 0x59f442)
await ctx.send(embed = em)
await self.bot.other.helpFunc(ctx, command)
# Lets you thank the bot
@cog_ext.cog_slash(**params["thank"])
@ -64,67 +38,51 @@ class MiscCog(commands.Cog):
# Sends a friendly message
@cog_ext.cog_slash(**params["hello"])
async def hello(self, ctx):
await ctx.send(self.bot.other.helloFunc(ctx.author.display_name))
await self.bot.other.helloFunc(ctx)
# Rolls dice
@cog_ext.cog_slash(**params["roll"])
async def roll(self, ctx, dice = "1d20"):
await ctx.send(self.bot.other.rollDice(ctx.author.display_name, dice))
await self.bot.other.rollDice(ctx, dice)
# Sends a random image
@cog_ext.cog_slash(**params["image"])
async def image(self, ctx):
await ctx.defer()
await ctx.send(self.bot.other.imageFunc())
await self.bot.other.imageFunc(ctx)
# Finds a random movie
@cog_ext.cog_slash(**params["movie"])
async def movie(self,ctx):
async def movie(self, ctx):
await self.bot.other.movieFunc(ctx)
# Generates a random name
@cog_ext.cog_slash(**params["name"])
async def name(self, ctx):
await ctx.send(self.generators.nameGen())
await self.generators.nameGen(ctx)
# Generates a random tavern name
@cog_ext.cog_slash(**params["tavern"])
async def tavern(self, ctx):
await ctx.send(self.generators.tavernGen())
await self.generators.tavernGen(ctx)
# Finds a page on the Senkulpa wiki
@cog_ext.cog_slash(**params["wiki"])
async def wiki(self, ctx, wikiPage):
await ctx.defer()
command = string.capwords(wikiPage)
title, content, thumbnail = self.bot.otherfindWikiPage(command)
if title != "":
self.bot.log("Sending the embedded message",str(ctx.channel_id))
content += "\n[Læs mere](https://senkulpa.fandom.com/da/wiki/"+title.replace(" ","_")+")"
embed = discord.Embed(title = title, description = content, colour=0xDEADBF)
if thumbnail != "":
embed.set_thumbnail(url=thumbnail)
await ctx.send(embed = embed)
else:
await ctx.send(content)
await self.bot.other.findWikiPage(ctx, wikiPage)
#Searches for movie and adds it to Bedre Netflix
@cog_ext.cog_slash(**params["addMovie"])
async def addMovie(self, ctx, movie):
await ctx.defer()
await self.bedreNetflix.requestMovie(ctx, movie)
#Searches for show and adds it to Bedre Netflix
@cog_ext.cog_slash(**params["addShow"])
async def addShow(self, ctx, show):
await ctx.defer()
await self.bedreNetflix.requestShow(ctx, show)
#Returns currently downloading torrents
@cog_ext.cog_slash(**params["downloading"])
async def downloading(self, ctx, parameters = "-d"):
await ctx.defer()
await self.bedreNetflix.downloading(ctx, parameters)
#Looks up on Wolfram Alpha

View File

@ -1,46 +0,0 @@
from discord.ext import commands
from utils import emojiToCommand
class ReactionCog(commands.Cog):
def __init__(self, bot):
"""Listens for reactions."""
self.bot = bot
@commands.Cog.listener()
async def on_reaction_add(self, reaction, user):
if user.bot == False:
message = reaction.message
channel = message.channel
self.bot.log(f"{user.display_name} reacted to a message",str(channel.id))
try:
connectFourTheirTurn, piece = self.bot.databaseFuncs.connectFourReactionTest(channel,message,"#"+str(user.id))
except:
connectFourTheirTurn = False
bedreNetflixMessage, addMovie, imdbIds = self.bot.databaseFuncs.bedreNetflixReactionTest(channel, message)
if connectFourTheirTurn:
place = emojiToCommand(reaction.emoji)
await self.bot.games.gameLoops.connectFour(message,"place "+str(piece)+" "+str(place),user.id, str(message.channel.id))
elif bedreNetflixMessage and addMovie:
moviePick = emojiToCommand(reaction.emoji)
await message.delete()
if moviePick == "none":
imdbID = None
else:
imdbID = imdbIds[moviePick-1]
await self.bot.other.bedreNetflix.addMovie(channel,imdbID)
elif bedreNetflixMessage and not addMovie:
showPick = emojiToCommand(reaction.emoji)
await message.delete()
if showPick == "none":
imdbName = None
else:
imdbName = imdbIds[showPick-1]
await self.bot.other.bedreNetflix.addShow(channel,imdbName)
elif self.bot.databaseFuncs.hangmanReactionTest(channel,message) and ord(reaction.emoji) in range(127462,127488):
guess = chr(ord(reaction.emoji)-127397)
await self.bot.games.gameLoops.runHangman(channel,"#"+str(user.id),command="guess "+guess)
def setup(bot):
bot.add_cog(ReactionCog(bot))

View File

@ -2,16 +2,9 @@ import discord, string, json
from discord.ext import commands
from discord_slash import cog_ext
from utils import Options, cap
from utils import getParams
with open("resources/slashParameters.json", "r") as f:
params = json.load(f)
options = Options()
if options.testing:
for p in params:
params[p]["guild_ids"] = options.guildIds
params = getParams()
class starWarsCog(commands.Cog):
@ -22,46 +15,23 @@ class starWarsCog(commands.Cog):
# Rolls star wars dice
@cog_ext.cog_slash(**params["starWarsRoll"])
async def starWarsRoll(self, ctx, dice = ""):
command = cap(dice)
newMessage = self.bot.starWars.roll.parseRoll("#"+str(ctx.author.id),command)
messageList = newMessage.split("\n")
await ctx.send(messageList[0])
if len(messageList) > 1:
for messageItem in messageList[1:]:
await ctx.channel.send(messageItem)
await self.bot.starWars.roll.parseRoll(ctx, dice)
# Controls destiny points
@cog_ext.cog_slash(**params["starWarsDestiny"])
async def starWarsDestiny(self, ctx, parameters = ""):
newMessage = self.bot.starWars.destiny.parseDestiny("#"+str(ctx.author.id),parameters)
messageList = newMessage.split("\n")
await ctx.send(messageList[0])
if len(messageList) > 1:
for messageItem in messageList[1:]:
await ctx.channel.send(messageItem)
await self.bot.starWars.destiny.parseDestiny(ctx, parameters)
# Rolls for critical injuries
@cog_ext.cog_slash(**params["starWarsCrit"])
async def starWarsCrit(self, ctx, severity : int = 0):
newMessage = self.bot.starWars.roll.critRoll(int(severity))
messageList = newMessage.split("\n")
await ctx.send(messageList[0])
if len(messageList) > 1:
for messageItem in messageList[1:]:
await ctx.channel.send(messageItem)
await self.bot.starWars.roll.critRoll(ctx, severity)
# Accesses and changes character sheet data with the parseChar function
# from funcs/starWarsFuncs/starWarsCharacter.py
@cog_ext.cog_slash(**params["starWarsCharacter"])
async def starWarsCharacter(self, ctx, parameters = ""):
command = string.capwords(parameters.replace("+","+ ").replace("-","- ").replace(",",", "))
title, desc = self.bot.starWars.character.parseChar("#"+str(ctx.author.id),command)
if title != "":
em1 = discord.Embed(title = title, description = desc, colour=0xDEADBF)
await ctx.send(embed = em1)
else:
await ctx.send(desc)
await self.bot.starWars.character.parseChar(ctx, parameters)
def setup(bot):
bot.add_cog(starWarsCog(bot))

View File

@ -3,4 +3,4 @@
__all__ = ["Money", "Games"]
from .money import Money
from .games import Games
from .gamesContainer import Games

View File

@ -18,7 +18,7 @@ class Blackjack():
def blackjackShuffle(self, decks, channel):
self.bot.log("Shuffling the blackjack deck")
with open("resources/games/deckofCards.txt","r") as f:
with open("resources/games/deckOfCards.txt","r") as f:
deck = f.read()
allDecks = deck.split("\n") * decks
@ -170,175 +170,223 @@ class Blackjack():
return hand, allStanding, preAllStanding
# When players try to hit
def blackjackHit(self,channel,user,handNumber = 0):
async def hit(self, ctx, handNumber = 0):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
channel = str(ctx.channel_id)
user = f"#{ctx.author.id}"
roundDone = False
game = self.bot.database["blackjack games"].find_one({"_id":channel})
if user in game["user hands"]:
hand, handNumber = self.getHandNumber(game["user hands"][user],handNumber)
userHands = game["user hands"][user]
hand, handNumber = self.getHandNumber(userHands, handNumber)
if hand != None:
if game["round"] > 0:
if hand["hit"] == False:
if hand["standing"] == False:
hand["hand"].append(self.drawCard(channel))
hand["hit"] = True
handValue = self.calcHandValue(hand["hand"])
if handValue > 21:
hand["busted"] = True
if handNumber == 2:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":hand}})
elif handNumber == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":hand}})
elif handNumber == 4:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".fourth hand":hand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:hand}})
response = "accept"
roundDone = self.isRoundDone(self.bot.database["blackjack games"].find_one({"_id":channel}))
return response + str(roundDone)[0] + str(game["round"])
else:
self.bot.log(user+" is already standing")
return "You can't hit when you're standing"
else:
self.bot.log(user+" has already hit this round")
return "You've already hit this round"
else:
self.bot.log(user+" tried to hit on the 0th round")
return "You can't hit before you see your cards"
if hand == None:
logMessage = "They didn't specify a hand"
sendMessage = "You need to specify a hand"
elif game["round"] <= 0:
logMessage = "They tried to hit on the 0th round"
sendMessage = "You can't hit before you see your cards"
elif hand["hit"]:
logMessage = "They've already hit this round"
sendMessage = "You've already hit this round"
elif hand["standing"]:
logMessage = "They're already standing"
sendMessage = "You can't hit when you're standing"
else:
self.bot.log(user+" didn't specify a hand")
return "You need to specify a hand"
else:
self.bot.log(user+" tried to hit without being in the game")
return "You have to enter the game before you can hit"
hand["hand"].append(self.drawCard(channel))
hand["hit"] = True
handValue = self.calcHandValue(hand["hand"])
if handValue > 21:
hand["busted"] = True
if handNumber == 2:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":hand}})
elif handNumber == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":hand}})
elif handNumber == 4:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".fourth hand":hand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:hand}})
roundDone = self.isRoundDone(self.bot.database["blackjack games"].find_one({"_id":channel}))
sendMessage = f"{ctx.author.display_name} hit"
logMessage = "They succeeded"
else:
logMessage = "They tried to hit without being in the game"
sendMessage = "You have to enter the game before you can hit"
await ctx.send(sendMessage)
self.bot.log(logMessage)
if roundDone:
gameID = game["gameID"]
self.bot.log("Hit calling self.blackjackLoop()", channel)
await self.blackjackLoop(ctx.channel, game["round"]+1, gameID)
# When players try to double down
def blackjackDouble(self,channel,user,handNumber = 0):
async def double(self, ctx, handNumber = 0):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
channel = str(ctx.channel_id)
user = f"#{ctx.author.id}"
roundDone = False
game = self.bot.database["blackjack games"].find_one({"_id":channel})
if user in game["user hands"]:
hand, handNumber = self.getHandNumber(game["user hands"][user],handNumber)
if hand != None:
if game["round"] > 0:
if hand["hit"] == False:
if hand["standing"] == False:
if len(hand["hand"]) == 2:
bet = hand["bet"]
if self.bot.money.checkBalance(user) >= bet:
self.bot.money.addMoney(user,-1 * bet)
hand["hand"].append(self.drawCard(channel))
hand["hit"] = True
hand["doubled"] = True
hand["bet"] += bet
handValue = self.calcHandValue(hand["hand"])
if handValue > 21:
hand["busted"] = True
if handNumber == 2:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":hand}})
elif handNumber == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":hand}})
elif handNumber == 4:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".fourth hand":hand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:hand}})
roundDone = self.isRoundDone(self.bot.database["blackjack games"].find_one({"_id":channel}))
return "Adding another "+str(bet)+" GwendoBucks to "+self.bot.databaseFuncs.getName(user)+"'s bet and drawing another card.",str(roundDone)[0] + str(game["round"])
else:
self.bot.log(user+" doesn't have enough GwendoBucks")
return "You don't have enough GwendoBucks",""
else:
self.bot.log(user+" tried to double on round "+str(game["round"]))
return "You can only double down on the first round",""
else:
self.bot.log(user+" is already standing")
return "You can't double when you're standing",""
else:
self.bot.log(user+" has already hit this round")
return "You've already hit this round",""
else:
self.bot.log(user+" tried to double on the 0th round")
return "You can't double down before you see your cards",""
if hand == None:
logMessage = "They didn't specify a hand"
sendMessage = "You need to specify a hand"
elif game["round"] <= 0:
logMessage = "They tried to hit on the 0th round"
sendMessage = "You can't hit before you see your cards"
elif hand["hit"]:
logMessage = "They've already hit this round"
sendMessage = "You've already hit this round"
elif hand["standing"]:
logMessage = "They're already standing"
sendMessage = "You can't hit when you're standing"
elif len(hand["hand"]) != 2:
logMessage = "They tried to double after round 1"
sendMessage = "You can only double on the first round"
else:
self.bot.log(user+" didn't specify a hand")
return "You need to specify which hand"
bet = hand["bet"]
if self.bot.money.checkBalance(user) < bet:
logMessage = "They tried to double without being in the game"
sendMessage = "You can't double when you're not in the game"
else:
self.bot.money.addMoney(user,-1 * bet)
hand["hand"].append(self.drawCard(channel))
hand["hit"] = True
hand["doubled"] = True
hand["bet"] += bet
handValue = self.calcHandValue(hand["hand"])
if handValue > 21:
hand["busted"] = True
if handNumber == 2:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":hand}})
elif handNumber == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":hand}})
elif handNumber == 4:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".fourth hand":hand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:hand}})
roundDone = self.isRoundDone(self.bot.database["blackjack games"].find_one({"_id":channel}))
sendMessage = f"Adding another {bet} GwendoBucks to {self.bot.databaseFuncs.getName(user)}'s bet and drawing another card."
logMessage = "They succeeded"
else:
self.bot.log(user+" tried to double without being in the game")
return "You can't double when you're not in the game",""
logMessage = "They tried to double without being in the game"
sendMessage = "You can't double when you're not in the game"
await ctx.send(sendMessage)
self.bot.log(logMessage)
if roundDone:
gameID = game["gameID"]
self.bot.log("Double calling self.blackjackLoop()", channel)
await self.blackjackLoop(ctx.channel, game["round"]+1, gameID)
# When players try to stand
def blackjackStand(self,channel,user,handNumber = 0):
async def stand(self, ctx, handNumber = 0):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
channel = str(ctx.channel_id)
user = f"#{ctx.author.id}"
roundDone = False
game = self.bot.database["blackjack games"].find_one({"_id":channel})
if user in game["user hands"]:
hand, handNumber = self.getHandNumber(game["user hands"][user],handNumber)
if hand != None:
if game["round"] > 0:
if hand["hit"] == False:
if hand["standing"] == False:
hand["standing"] = True
if handNumber == 2:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":hand}})
elif handNumber == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":hand}})
elif handNumber == 4:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".fourth hand":hand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:hand}})
response = "accept"
roundDone = self.isRoundDone(self.bot.database["blackjack games"].find_one({"_id":channel}))
return response + str(roundDone)[0] + str(game["round"])
else:
self.bot.log(user+" is already standing")
return "You're already standing"
else:
self.bot.log(user+" has already hit this round")
return "You've already hit this round"
else:
self.bot.log(user+" tried to stand on the first round")
return "You can't stand before you see your cards"
if hand == None:
sendMessage = "You need to specify which hand"
logMessage = "They didn't specify a hand"
elif game["round"] <= 0:
sendMessage = "You can't stand before you see your cards"
logMessage = "They tried to stand on round 0"
elif hand["hit"]:
sendMessage = "You've already hit this round"
logMessage = "They'd already hit this round"
elif hand["standing"]:
sendMessage = "You're already standing"
logMessage = "They're already standing"
else:
self.bot.log(user+" didn't specify a hand")
return "You need to specify which hand"
hand["standing"] = True
if handNumber == 2:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":hand}})
elif handNumber == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":hand}})
elif handNumber == 4:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".fourth hand":hand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:hand}})
roundDone = self.isRoundDone(self.bot.database["blackjack games"].find_one({"_id":channel}))
sendMessage = f"{ctx.author.display_name} is standing"
logMessage = "They succeeded"
else:
self.bot.log(user+" tried to stand without being in the game")
return "You have to enter the game before you can stand"
logMessage = "They tried to stand without being in the game"
sendMessage = "You have to enter the game before you can stand"
await ctx.send(sendMessage)
self.bot.log(logMessage)
if roundDone:
gameID = game["gameID"]
self.bot.log("Stand calling self.blackjackLoop()", channel)
await self.blackjackLoop(ctx.channel, game["round"]+1, gameID)
# When players try to split
def blackjackSplit(self,channel,user,handNumber = 0):
async def split(self, ctx, handNumber = 0):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
channel = str(ctx.channel_id)
user = f"#{ctx.author.id}"
roundDone = False
handNumberError = False
game = self.bot.database["blackjack games"].find_one({"_id":channel})
if game["user hands"][user]["split"] == 0:
@ -347,166 +395,200 @@ class Blackjack():
handNumber = 0
otherHand = 2
else:
if handNumber != 0:
if handNumber == 1:
hand = game["user hands"][user]
elif handNumber == 2:
hand = game["user hands"][user]["other hand"]
elif handNumber == 3:
hand = game["user hands"][user]["third hand"]
else:
self.bot.log(user+" tried to hit without specifying which hand")
return "You have to specify the hand you're hitting with."
if game["user hands"][user]["split"] == 1:
newHand = game["user hands"][user]["third hand"]
otherHand = 3
else:
newHand = game["user hands"][user]["fourth hand"]
otherHand = 4
if handNumber == 1:
hand = game["user hands"][user]
elif handNumber == 2:
hand = game["user hands"][user]["other hand"]
elif handNumber == 3:
hand = game["user hands"][user]["third hand"]
else:
self.bot.log(user+" tried to split without specifying which hand")
return "You have to specify the hand you're splitting.",""
handNumberError = True
if game["user hands"][user]["split"] < 3:
if game["round"] != 0:
if hand["hit"] == False:
if hand["standing"] == False:
if len(hand["hand"]) == 2:
firstCard = self.calcHandValue([hand["hand"][0]])
secondCard = self.calcHandValue([hand["hand"][1]])
if firstCard == secondCard:
bet = hand["bet"]
if self.bot.money.checkBalance(user) >= bet:
self.bot.money.addMoney(user,-1 * bet)
hand["hit"] = True
newHand["hit"] = True
newHand = {
"hand":[],"bet":0,"standing":False,"busted":False,
"blackjack":False,"hit":True,"doubled":False}
newHand["bet"] = hand["bet"]
newHand["hand"].append(hand["hand"].pop(1))
newHand["hand"].append(self.drawCard(channel))
hand["hand"].append(self.drawCard(channel))
handValue = self.calcHandValue(hand["hand"])
otherHandValue = self.calcHandValue(newHand["hand"])
if handValue > 21:
hand["busted"] = True
elif handValue == 21:
hand["blackjack"] = True
if otherHandValue > 21:
newHand["busted"] = True
elif otherHandValue == 21:
newHand["blackjack"] = True
if handNumber == 2:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":hand}})
elif handNumber == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":hand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:hand}})
if otherHand == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":newHand}})
elif otherHand == 4:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".fourth hand":newHand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":newHand}})
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$inc":{"user hands."+user+".split":1}})
roundDone = self.isRoundDone(self.bot.database["blackjack games"].find_one({"_id":channel}))
return "Splitting "+self.bot.databaseFuncs.getName(user)+"'s hand into 2. Adding their original bet to the second hand. You can use \"/blackjack hit/stand/double 1\" and \"/blackjack hit/stand/double 2\" to play the different hands.",str(roundDone)[0] + str(game["round"])
else:
self.bot.log(user+" doesn't have enough GwendoBucks")
return "You don't have enough GwendoBucks",""
else:
self.bot.log(user+" tried to split 2 different cards")
return "Your cards need to have the same value to split",""
else:
self.bot.log(user+" tried to split later than they could")
return "You can only split on the first round",""
else:
self.bot.log(user+" is already standing")
return "You can't split when you're standing",""
else:
self.bot.log(user+" has already hit this round")
return "You've already hit this round",""
if game["user hands"][user]["split"] == 1:
newHand = game["user hands"][user]["third hand"]
otherHand = 3
else:
self.bot.log(user+" tried to split on the 0th round")
return "You can't split before you see your cards",""
newHand = game["user hands"][user]["fourth hand"]
otherHand = 4
if handNumberError:
logMessage = "They didn't specify a hand"
sendMessage = "You have to specify the hand you're hitting with"
elif game["round"] == 0:
logMessage = "They tried to split on round 0"
sendMessage = "You can't split before you see your cards"
elif game["user hands"][user]["split"] > 3:
logMessage = "They tried to split more than three times"
sendMessage = "You can only split 3 times"
elif hand["hit"]:
logMessage = "They've already hit"
sendMessage = "You've already hit"
elif hand["standing"]:
logMessage = "They're already standing"
sendMessage = "You're already standing"
elif len(hand["hand"]) != 2:
logMessage = "They tried to split after the first round"
sendMessage = "You can only split on the first round"
else:
self.bot.log(user+" tried to split more than three times")
return "You can only split 3 times",""
firstCard = self.calcHandValue([hand["hand"][0]])
secondCard = self.calcHandValue([hand["hand"][1]])
if firstCard != secondCard:
logMessage = "They tried to split two different cards"
sendMessage = "You can only split if your cards have the same value"
else:
bet = hand["bet"]
if self.bot.money.checkBalance(user) < bet:
logMessage = "They didn't have enough GwendoBucks"
sendMessage = "You don't have enough GwendoBucks"
else:
self.bot.money.addMoney(user,-1 * bet)
hand["hit"] = True
newHand["hit"] = True
newHand = {
"hand":[],"bet":0,"standing":False,"busted":False,
"blackjack":False,"hit":True,"doubled":False}
newHand["bet"] = hand["bet"]
newHand["hand"].append(hand["hand"].pop(1))
newHand["hand"].append(self.drawCard(channel))
hand["hand"].append(self.drawCard(channel))
handValue = self.calcHandValue(hand["hand"])
otherHandValue = self.calcHandValue(newHand["hand"])
if handValue > 21:
hand["busted"] = True
elif handValue == 21:
hand["blackjack"] = True
if otherHandValue > 21:
newHand["busted"] = True
elif otherHandValue == 21:
newHand["blackjack"] = True
if handNumber == 2:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":hand}})
elif handNumber == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":hand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:hand}})
if otherHand == 3:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".third hand":newHand}})
elif otherHand == 4:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".fourth hand":newHand}})
else:
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user+".other hand":newHand}})
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$inc":{"user hands."+user+".split":1}})
roundDone = self.isRoundDone(self.bot.database["blackjack games"].find_one({"_id":channel}))
sendMessage = f"Splitting {self.bot.databaseFuncs.getName(user)}'s hand into 2. Adding their original bet to the second hand. You can use \"/blackjack hit/stand/double 1\" and \"/blackjack hit/stand/double 2\" to play the different hands."
logMessage = "They succeeded"
await ctx.send(sendMessage)
self.bot.log(logMessage)
if roundDone:
gameID = game["gameID"]
self.bot.log("Stand calling self.blackjackLoop()", channel)
await self.blackjackLoop(ctx.channel, game["round"]+1, gameID)
# Player enters the game and draws a hand
def blackjackPlayerDrawHand(self,channel,user,bet):
game = self.bot.database["blackjack games"].find_one({"_id":channel})
async def playerDrawHand(self, ctx, bet : int):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
channel = str(ctx.channel_id)
user = f"#{ctx.author.id}"
collection = self.bot.database["blackjack games"]
game = collection.find_one({"_id":channel})
userName = self.bot.databaseFuncs.getName(user)
self.bot.log(self.bot.databaseFuncs.getName(user)+" is trying to join the game in "+channel)
self.bot.log(f"{userName} is trying to join the Blackjack game")
if game != None:
if user not in game["user hands"]:
if len(game["user hands"]) < 5:
if game["round"] == 0:
if bet >= 0:
if self.bot.money.checkBalance(user) >= bet:
self.bot.money.addMoney(user,-1 * bet)
playerHand = [self.drawCard(channel),self.drawCard(channel)]
handValue = self.calcHandValue(playerHand)
if handValue == 21:
blackjackHand = True
else:
blackjackHand = False
newHand = {"hand":playerHand,
"bet":bet,"standing":False,"busted":False,"blackjack":blackjackHand,"hit":True,
"doubled":False,"split":0,"other hand":{},"third hand":{},"fourth hand":{}}
self.bot.database["blackjack games"].update_one({"_id":channel},
{"$set":{"user hands."+user:newHand}})
self.bot.log(f"{self.bot.databaseFuncs.getName(user)} entered the game with a bet of {bet}")
return f"{self.bot.databaseFuncs.getName(user)} entered the game with a bet of {bet}"
else:
self.bot.log(user+" doesn't have enough GwendoBucks")
return "You don't have enough GwendoBucks to place that bet"
else:
self.bot.log(user+" tried to bet a negative amount")
return "You can't bet a negative amount"
else:
self.bot.log("The table is no longer open for bets")
return "The table is no longer open for bets"
else:
self.bot.log("There are already 5 players in the game.")
return "There's already a maximum of players at the table."
else:
self.bot.log(user+" is already in the game")
return "You've already entered this game"
if game == None:
sendMessage = "There is no game going on in this channel"
logMessage = sendMessage
elif user in game["user hands"]:
sendMessage = "You're already in the game!"
logMessage = "They're already in the game"
elif len(game["user hands"]) >= 5:
sendMessage = "There can't be more than 5 players in a game"
logMessage = "There were already 5 players in the game"
elif game["round"] != 0:
sendMessage = "The table is no longer taking bets"
logMessage = "They tried to join after the game begun"
elif bet < 0:
sendMessage = "You can't bet a negative amount"
logMessage = "They tried to bet a negative amount"
elif self.bot.money.checkBalance(user) < bet:
sendMessage = "You don't have enough GwendoBucks"
logMessage = "They didn't have enough GwendoBucks"
else:
self.bot.log("There is no game going on in "+channel)
return "There is no game going on in this channel"
self.bot.money.addMoney(user,-1 * bet)
playerHand = [self.drawCard(channel) for _ in range(2)]
handValue = self.calcHandValue(playerHand)
if handValue == 21:
blackjackHand = True
else:
blackjackHand = False
newHand = {"hand":playerHand, "bet":bet, "standing":False,
"busted":False, "blackjack":blackjackHand, "hit":True,
"doubled":False, "split":0, "other hand":{},
"third hand":{}, "fourth hand":{}}
function = {"$set":{f"user hands.{user}":newHand}}
collection.update_one({"_id":channel}, function)
enterGameText = "entered the game with a bet of"
betText = f"{bet} GwendoBucks"
sendMessage = f"{userName} {enterGameText} {betText}"
logMessage = sendMessage
self.bot.log(sendMessage)
await ctx.send(logMessage)
# Starts a game of blackjack
def blackjackStart(self,channel:str):
async def start(self, ctx):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
channel = str(ctx.channel_id)
blackjackMinCards = 50
blackjackDecks = 4
await ctx.send("Starting a new game of blackjack")
cardsLeft = 0
cards = self.bot.database["blackjack cards"].find_one({"_id":channel})
if cards != None:
cardsLeft = len(cards["cards"])
# Shuffles if not enough cards
if cardsLeft < blackjackMinCards:
self.blackjackShuffle(blackjackDecks, channel)
self.bot.log("Shuffling the blackjack deck...", channel)
await ctx.channel.send("Shuffling the deck...")
game = self.bot.database["blackjack games"].find_one({"_id":channel})
self.bot.log("Trying to start a blackjack game in "+channel)
gameStarted = False
if game == None:
@ -522,10 +604,39 @@ class Blackjack():
copyfile("resources/games/blackjackTable.png","resources/games/blackjackTables/blackjackTable"+channel+".png")
return "started"
gameStarted = True
if gameStarted:
sendMessage = "Blackjack game started. Use \"/blackjack bet [amount]\" to enter the game within the next 30 seconds."
await ctx.channel.send(sendMessage)
filePath = f"resources/games/blackjackTables/blackjackTable{channel}.png"
oldImage = await ctx.channel.send(file = discord.File(filePath))
with open("resources/games/oldImages/blackjack"+channel, "w") as f:
f.write(str(oldImage.id))
await asyncio.sleep(30)
gamedone = False
game = self.bot.database["blackjack games"].find_one({"_id":str(channel)})
if len(game["user hands"]) == 0:
gamedone = True
await ctx.channel.send("No one entered the game. Ending the game.")
gameID = game["gameID"]
# Loop of game rounds
if gamedone == False:
self.bot.log("start() calling blackjackLoop()", channel)
await self.blackjackLoop(ctx.channel,1,gameID)
else:
new_message = self.blackjackFinish(channel)
await ctx.channel.send(new_message)
else:
self.bot.log("There is already a blackjack game going on in "+channel)
return "There's already a blackjack game going on. Try again in a few minutes."
await ctx.channel.send("There's already a blackjack game going on. Try again in a few minutes.")
self.bot.log("There was already a game going on")
# Ends the game and calculates winnings
def blackjackFinish(self,channel):
@ -562,7 +673,6 @@ class Blackjack():
return finalWinnings
def calcWinnings(self,hand, dealerValue, topLevel, dealerBlackjack, dealerBusted):
self.bot.log("Calculating winnings")
reason = ""
@ -703,178 +813,34 @@ class Blackjack():
else:
self.bot.log("Ending loop on round "+str(gameRound),str(channel.id))
async def parseBlackjack(self,content, ctx):
# Blackjack shuffle variables
blackjackMinCards = 50
blackjackDecks = 4
# Returning current hi-lo value
async def hilo(self, ctx):
channel = ctx.channel_id
# Starts the game
if content == "":
await ctx.send("Starting a new game of blackjack")
cardsLeft = 0
cards = self.bot.database["blackjack cards"].find_one({"_id":str(channel)})
if cards != None:
cardsLeft = len(cards["cards"])
# Shuffles if not enough cards
if cardsLeft < blackjackMinCards:
self.blackjackShuffle(blackjackDecks,str(channel))
self.bot.log("Shuffling the blackjack deck...",str(channel))
await ctx.channel.send("Shuffling the deck...")
new_message = self.blackjackStart(str(channel))
if new_message == "started":
new_message = "Blackjack game started. Use \"/blackjack bet [amount]\" to enter the game within the next 30 seconds."
await ctx.channel.send(new_message)
oldImage = await ctx.channel.send(file = discord.File("resources/games/blackjackTables/blackjackTable"+str(channel)+".png"))
with open("resources/games/oldImages/blackjack"+str(channel), "w") as f:
f.write(str(oldImage.id))
await asyncio.sleep(30)
gamedone = False
game = self.bot.database["blackjack games"].find_one({"_id":str(channel)})
if len(game["user hands"]) == 0:
gamedone = True
await ctx.channel.send("No one entered the game. Ending the game.")
gameID = game["gameID"]
# Loop of game rounds
if gamedone == False:
self.bot.log("/blackjack calling self.blackjackLoop()",str(channel))
await self.blackjackLoop(ctx.channel,1,gameID)
else:
new_message = self.blackjackFinish(str(channel))
await ctx.channel.send(new_message)
else:
await ctx.channel.send(new_message)
# Entering game and placing bet
elif content.startswith("bet"):
commands = content.split(" ")
amount = int(commands[1])
response = self.blackjackPlayerDrawHand(str(channel),"#"+str(ctx.author.id),amount)
await ctx.send(response)
# Hitting
elif content.startswith("hit"):
if content == "hit":
response = self.blackjackHit(str(channel),"#"+str(ctx.author.id))
else:
commands = content.split(" ")
try:
handNumber = int(commands[1])
except:
handNumber = 0
response = self.blackjackHit(str(channel),"#"+str(ctx.author.id),handNumber)
if response.startswith("accept"):
await ctx.send(f"{ctx.author.display_name} hit")
#try:
if response[6] == "T":
gameID = self.bot.database["blackjack games"].find_one({"_id":str(channel)})["gameID"]
self.bot.log("Hit calling self.blackjackLoop()",str(channel))
await self.blackjackLoop(ctx.channel,int(response[7:])+1,gameID)
#except:
# self.bot.log("Something fucked up (error code 1320)",str(channel))
else:
await ctx.send(response)
# Standing
elif content.startswith("stand"):
if content == "hit":
response = self.blackjackStand(str(channel),"#"+str(ctx.author.id))
else:
commands = content.split(" ")
try:
handNumber = int(commands[1])
except:
handNumber = 0
response = self.blackjackStand(str(channel),"#"+str(ctx.author.id),handNumber)
if response.startswith("accept"):
await ctx.send(f"{ctx.author.display_name} is standing")
#try:
if response[6] == "T":
gameID = self.bot.database["blackjack games"].find_one({"_id":str(channel)})["gameID"]
self.bot.log("Stand calling self.blackjackLoop()",str(channel))
await self.blackjackLoop(ctx.channel,int(response[7:])+1,gameID)
#except:
# self.bot.log("Something fucked up (error code 1320)",str(channel))
else:
await ctx.send(response)
# Doubling bet
elif content.startswith("double"):
commands = content.split(" ")
try:
handNumber = int(commands[1])
except:
handNumber = 0
response, roundDone = self.blackjackDouble(str(channel),"#"+str(ctx.author.id),handNumber)
await ctx.send(response)
try:
if roundDone[0] == "T":
gameID = self.bot.database["blackjack games"].find_one({"_id":str(channel)})["gameID"]
self.bot.log("Double calling self.blackjackLoop()",str(channel))
await self.blackjackLoop(ctx.channel,int(roundDone[1:])+1,gameID)
except:
self.bot.log("Something fucked up (error code 1320)",str(channel))
# Splitting hand
elif content.startswith("split"):
commands = content.split(" ")
try:
handNumber = int(commands[1])
except:
handNumber = 0
response, roundDone = self.blackjackSplit(str(channel),"#"+str(ctx.author.id),handNumber)
await ctx.send(response)
try:
if roundDone[0] == "T":
gameID = self.bot.database["blackjack games"].find_one({"_id":str(channel)})["gameID"]
self.bot.log("Split calling self.blackjackLoop()",str(channel))
await self.blackjackLoop(ctx.channel,int(roundDone[1:])+1,gameID)
except:
self.bot.log("Something fucked up (error code 1320)")
# Returning current hi-lo value
elif content.startswith("hilo"):
data = self.bot.database["hilo"].find_one({"_id":str(channel)})
if data != None:
hilo = str(data["hilo"])
else:
hilo = "0"
await ctx.send(hilo, hidden=True)
# Shuffles the blackjack deck
elif content.startswith("shuffle"):
self.blackjackShuffle(blackjackDecks,str(channel))
self.bot.log("Shuffling the blackjack deck...",str(channel))
await ctx.send("Shuffling the deck...")
# Tells you the amount of cards left
elif content.startswith("cards"):
cardsLeft = 0
cards = self.bot.database["blackjack cards"].find_one({"_id":str(channel)})
if cards != None:
cardsLeft = len(cards["cards"])
decksLeft = round(cardsLeft/52,1)
await ctx.send(str(cardsLeft)+" cards, "+str(decksLeft)+" decks", hidden=True)
data = self.bot.database["hilo"].find_one({"_id":str(channel)})
if data != None:
hilo = str(data["hilo"])
else:
self.bot.log("Not a command (error code 1301)")
await ctx.send("I didn't quite understand that (error code 1301)")
hilo = "0"
await ctx.send(f"Hi-lo value: {hilo}", hidden=True)
# Shuffles the blackjack deck
async def shuffle(self, ctx):
blackjackDecks = 4
channel = ctx.channel_id
self.blackjackShuffle(blackjackDecks,str(channel))
self.bot.log("Shuffling the blackjack deck...",str(channel))
await ctx.send("Shuffling the deck...")
# Tells you the amount of cards left
async def cards(self, ctx):
channel = ctx.channel_id
cardsLeft = 0
cards = self.bot.database["blackjack cards"].find_one({"_id":str(channel)})
if cards != None:
cardsLeft = len(cards["cards"])
decksLeft = round(cardsLeft/52,1)
await ctx.send(f"Cards left:\n{cardsLeft} cards, {decksLeft} decks", hidden=True)

View File

@ -1,10 +1,11 @@
import random
import copy
import math
import discord
from .connectFourDraw import drawConnectFour
AIScores = {
AISCORES = {
"middle": 3,
"two in a row": 10,
"three in a row": 50,
@ -15,75 +16,127 @@ AIScores = {
"avoid losing": 100
}
rowCount = 6
columnCount = 7
easy = True
ROWCOUNT = 6
COLUMNCOUNT = 7
class connectFour():
class ConnectFour():
def __init__(self,bot):
self.bot = bot
self.draw = drawConnectFour(bot)
# Starts the game
def connectFourStart(self, channel, user, opponent):
async def start(self, ctx, opponent):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
user = f"#{ctx.author.id}"
channel = str(ctx.channel_id)
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
if game == None:
startedGame = False
canStart = True
if opponent in ["1","2","3","4","5"]:
difficulty = int(opponent)
diffText = " with difficulty "+opponent
opponent = "Gwendolyn"
elif opponent.lower() == "gwendolyn":
difficulty = 3
diffText = " with difficulty 3"
opponent = "Gwendolyn"
else:
try:
int(opponent)
return "That difficulty doesn't exist", False, False, False, False
except:
if game != None:
sendMessage = "There's already a connect 4 game going on in this channel"
logMessage = "There was already a game going on"
canStart = False
else:
if type(opponent) == int:
# Opponent is Gwendolyn
if opponent in range(1, 6):
difficulty = int(opponent)
diffText = f" with difficulty {difficulty}"
opponent = f"#{self.bot.user.id}"
else:
sendMessage = "Difficulty doesn't exist"
logMessage = "They tried to play against a difficulty that doesn't exist"
canStart = False
elif type(opponent) == discord.member.Member:
if opponent.bot:
# User has challenged a bot
if opponent == self.bot.user:
# It was Gwendolyn
difficulty = 3
diffText = f" with difficulty {difficulty}"
opponent = f"#{self.bot.user.id}"
else:
sendMessage = "You can't challenge a bot!"
logMessage = "They tried to challenge a bot"
canStart = False
else:
# Opponent is another player
opponent = self.bot.databaseFuncs.getID(opponent)
if opponent != None:
if ctx.author != opponent:
opponent = f"#{opponent.id}"
difficulty = 5
diffText = ""
else:
return "I can't find that user", False, False, False, False
sendMessage = "You can't play against yourself"
logMessage = "They tried to play against themself"
canStart = False
if user == opponent:
return "You can't play against yourself", False, False, False, False
board = [ [ 0 for i in range(columnCount) ] for j in range(rowCount) ]
players = [user,opponent]
if canStart:
board = [[0 for _ in range(COLUMNCOUNT)] for _ in range(ROWCOUNT)]
players = [user, opponent]
random.shuffle(players)
newGame = {"_id":channel,"board": board,"winner":0,"win direction":"",
"win coordinates":[0,0],"players":players,"turn":0,"difficulty":difficulty}
newGame = {"_id":channel, "board": board, "winner":0,
"win direction":"", "win coordinates":[0, 0],
"players":players, "turn":0, "difficulty":difficulty}
self.bot.database["connect 4 games"].insert_one(newGame)
self.draw.drawImage(channel)
gwendoTurn = False
gwendoTurn = (players[0] == f"#{self.bot.user.id}")
startedGame = True
if players[0] == "Gwendolyn":
gwendoTurn = True
opponentName = self.bot.databaseFuncs.getName(opponent)
turnName = self.bot.databaseFuncs.getName(players[0])
return "Started game against "+self.bot.databaseFuncs.getName(opponent)+diffText+". It's "+self.bot.databaseFuncs.getName(players[0])+"'s turn", True, False, False, gwendoTurn
else:
return "There's already a connect 4 game going on in this channel", False, False, False, False
startedText = f"Started game against {opponentName}{diffText}."
turnText = f"It's {turnName}'s turn"
sendMessage = f"{startedText} {turnText}"
logMessage = "They started a game"
self.bot.log(logMessage)
await ctx.send(sendMessage)
# Sets the whole game in motion
if startedGame:
filePath = f"resources/games/connect4Boards/board{ctx.channel_id}.png"
oldImage = await ctx.channel.send(file = discord.File(filePath))
with open(f"resources/games/oldImages/connectFour{ctx.channel_id}", "w") as f:
f.write(str(oldImage.id))
if gwendoTurn:
await self.connectFourAI(ctx)
else:
reactions = ["1","2","3","4","5","6","7"]
for reaction in reactions:
await oldImage.add_reaction(reaction)
# Places a piece at the lowest available point in a specific column
def placePiece(self, channel : str,player : int,column : int):
async def placePiece(self, ctx, user, column):
channel = str(ctx.channel.id)
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
playerNumber = game["players"].index(user)+1
userName = self.bot.databaseFuncs.getName(user)
placedPiece = False
if game != None:
if game is None:
sendMessage = "There's no game in this channel"
logMessage = "There was no game in the channel"
else:
board = game["board"]
board = self.placeOnBoard(board, playerNumber, column)
board = self.placeOnBoard(board,player,column)
if board != None:
if board is None:
sendMessage = "There isn't any room in that column"
logMessage = "There wasn't any room in the column"
else:
self.bot.database["connect 4 games"].update_one({"_id":channel},{"$set":{"board":board}})
turn = (game["turn"]+1)%2
self.bot.database["connect 4 games"].update_one({"_id":channel},{"$set":{"turn":turn}})
@ -98,82 +151,115 @@ class connectFour():
self.bot.database["connect 4 games"].update_one({"_id":channel},
{"$set":{"win coordinates":winCoordinates}})
message = self.bot.databaseFuncs.getName(game["players"][won-1])+" placed a piece in column "+str(column+1)+" and won."
sendMessage = f"{userName} placed a piece in column {column+1} and won."
logMessage = f"{userName} won"
winAmount = int(game["difficulty"])**2+5
if game["players"][won-1] != "Gwendolyn":
message += " Adding "+str(winAmount)+" GwendoBucks to their account."
if game["players"][won-1] != f"#{self.bot.user.id}":
sendMessage += " Adding "+str(winAmount)+" GwendoBucks to their account"
elif 0 not in board[0]:
gameWon = True
message = "It's a draw!"
sendMessage = "It's a draw!"
logMessage = "The game ended in a draw"
else:
gameWon = False
message = self.bot.databaseFuncs.getName(game["players"][player-1])+" placed a piece in column "+str(column+1)+". It's now "+self.bot.databaseFuncs.getName(game["players"][turn])+"'s turn."
otherUserName = self.bot.databaseFuncs.getName(game["players"][turn])
sendMessage = f"{userName} placed a piece in column {column+1}. It's now {otherUserName}'s turn"
logMessage = "They placed the piece"
gwendoTurn = False
gwendoTurn = (game["players"][turn] == f"#{self.bot.user.id}")
if game["players"][turn] == "Gwendolyn":
self.bot.log("It's Gwendolyn's turn")
gwendoTurn = True
placedPiece = True
self.draw.drawImage(channel)
return message, True, True, gameWon, gwendoTurn
await ctx.channel.send(sendMessage)
self.bot.log(logMessage)
if placedPiece:
self.draw.drawImage(channel)
with open(f"resources/games/oldImages/connectFour{channel}", "r") as f:
oldImage = await ctx.channel.fetch_message(int(f.read()))
if oldImage is not None:
await oldImage.delete()
else:
return "There isn't any room in that column", True, True, False, False
else:
return "There's no game in this channel", False, False, False, False
self.bot.log("The old image was already deleted")
filePath = f"resources/games/connect4Boards/board{channel}.png"
oldImage = await ctx.channel.send(file = discord.File(filePath))
if gameWon:
self.endGame(channel)
else:
with open(f"resources/games/oldImages/connectFour{channel}", "w") as f:
f.write(str(oldImage.id))
if gwendoTurn:
await self.connectFourAI(ctx)
else:
reactions = ["1","2","3","4","5","6","7"]
for reaction in reactions:
await oldImage.add_reaction(reaction)
# Returns a board where a piece has been placed in the column
def placeOnBoard(self,board,player,column):
placementx, placementy = -1, column
def placeOnBoard(self, board, player, column):
placementX, placementY = -1, column
for x, line in enumerate(board):
if line[column] == 0:
placementx = x
placementX = x
board[placementx][placementy] = player
board[placementX][placementY] = player
if placementx == -1:
if placementX == -1:
return None
else:
return board
def endGame(self, channel):
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
winner = game["winner"]
if winner != 0:
if game["players"][winner-1] != f"#{self.bot.user.id}":
difficulty = int(game["difficulty"])
reward = difficulty**2 + 5
self.bot.money.addMoney(game["players"][winner-1], reward)
self.bot.databaseFuncs.deleteGame("connect 4 games", channel)
# Parses command
def parseconnectFour(self, command, channel, user):
commands = command.split()
if command == "" or command == " ":
return "I didn't get that. Use \"/connectFour start [opponent]\" to start a game. To play against the computer, use difficulty 1 through 5 as the [opponent].", False, False, False, False
elif commands[0] == "start":
# Starting a game
if len(commands) == 1: # if the commands is "/connectFour start", the opponent is Gwendolyn
commands.append("3")
return self.connectFourStart(channel,user,commands[1]) # commands[1] is the opponent
async def surrender(self, ctx):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
channel = str(ctx.channel_id)
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
# Stopping the game
elif commands[0] == "stop":
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
if f"#{ctx.author.id}" in game["players"]:
loserIndex = game["players"].index(f"#{ctx.author.id}")
winnerIndex = (loserIndex+1)%2
winnerID = game["players"][winnerIndex]
winnerName = self.bot.databaseFuncs.getName(winnerID)
if user in game["players"]:
return "Ending game.", False, False, True, False
sendMessage = f"{ctx.author.display_name} surrenders."
sendMessage += f" This means {winnerName} is the winner."
if winnerID != f"#{self.bot.user.id}":
difficulty = int(game["difficulty"])
reward = difficulty**2 + 5
sendMessage += f" Adding {reward} to their account"
await ctx.send(sendMessage)
with open(f"resources/games/oldImages/connectFour{channel}", "r") as f:
oldImage = await ctx.channel.fetch_message(int(f.read()))
if oldImage is not None:
await oldImage.delete()
else:
return "You can't end a game where you're not a player.", False, False, False, False
self.bot.log("The old image was already deleted")
# Placing manually
elif commands[0] == "place":
if len(commands) == 2:
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
turn = game["turn"]
if user == game["players"][turn]:
piece = turn + 1
else:
self.bot.log("It wasn't their turn")
return "It's not your turn!", False, False, False, False
column = int(commands[1])-1
else:
column = int(commands[2])-1
piece = int(commands[1])
return self.placePiece(channel, piece, column)
self.endGame(channel)
else:
return "I didn't get that. Use \"/connectFour start [opponent]\" to start a game. To play against the computer, use difficulty 1 through 5 as the [opponent].", False, False, False, False
await ctx.send("You can't surrender when you're not a player")
# Checks if someone has won the game and returns the winner
def isWon(self, board):
@ -181,13 +267,13 @@ class connectFour():
winDirection = ""
winCoordinates = [0,0]
for row in range(rowCount):
for place in range(columnCount):
for row in range(ROWCOUNT):
for place in range(COLUMNCOUNT):
if won == 0:
piecePlayer = board[row][place]
if piecePlayer != 0:
# Checks horizontal
if place <= columnCount-4:
if place <= COLUMNCOUNT-4:
pieces = [board[row][place+1],board[row][place+2],board[row][place+3]]
else:
pieces = [0]
@ -198,7 +284,7 @@ class connectFour():
winCoordinates = [row,place]
# Checks vertical
if row <= rowCount-4:
if row <= ROWCOUNT-4:
pieces = [board[row+1][place],board[row+2][place],board[row+3][place]]
else:
pieces = [0]
@ -209,7 +295,7 @@ class connectFour():
winCoordinates = [row,place]
# Checks right diagonal
if row <= rowCount-4 and place <= columnCount-4:
if row <= ROWCOUNT-4 and place <= COLUMNCOUNT-4:
pieces = [board[row+1][place+1],board[row+2][place+2],board[row+3][place+3]]
else:
pieces = [0]
@ -220,7 +306,7 @@ class connectFour():
winCoordinates = [row,place]
# Checks left diagonal
if row <= rowCount-4 and place >= 3:
if row <= ROWCOUNT-4 and place >= 3:
pieces = [board[row+1][place-1],board[row+2][place-2],board[row+3][place-3]]
else:
pieces = [0]
@ -234,21 +320,23 @@ class connectFour():
return won, winDirection, winCoordinates
# Plays as the AI
async def connectFourAI(self, channel):
async def connectFourAI(self, ctx):
channel = str(ctx.channel.id)
self.bot.log("Figuring out best move")
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
board = game["board"]
player = game["players"].index("Gwendolyn")+1
player = game["players"].index(f"#{self.bot.user.id}")+1
difficulty = game["difficulty"]
scores = [-math.inf,-math.inf,-math.inf,-math.inf,-math.inf,-math.inf,-math.inf]
for column in range(0,columnCount):
scores = [-math.inf for _ in range(COLUMNCOUNT)]
for column in range(COLUMNCOUNT):
testBoard = copy.deepcopy(board)
testBoard = self.placeOnBoard(testBoard,player,column)
if testBoard != None:
scores[column] = await self.minimax(testBoard,difficulty,player%2+1,player,-math.inf,math.inf,False)
self.bot.log("Best score for column "+str(column)+" is "+str(scores[column]))
self.bot.log(f"Best score for column {column} is {scores[column]}")
possibleScores = scores.copy()
@ -257,9 +345,10 @@ class connectFour():
highest_score = random.choice(possibleScores)
indices = [i for i, x in enumerate(scores) if x == highest_score]
placement = random.choice(indices)
return self.placePiece(channel,player,placement)
bestColumns = [i for i, x in enumerate(scores) if x == highest_score]
placement = random.choice(bestColumns)
await self.placePiece(ctx, f"#{self.bot.user.id}", placement)
# Calculates points for a board
def AICalcPoints(self,board,player):
@ -268,28 +357,28 @@ class connectFour():
# Adds points for middle placement
# Checks horizontal
for row in range(rowCount):
for row in range(ROWCOUNT):
if board[row][3] == player:
score += AIScores["middle"]
score += AISCORES["middle"]
rowArray = [int(i) for i in list(board[row])]
for place in range(columnCount-3):
for place in range(COLUMNCOUNT-3):
window = rowArray[place:place+4]
score += self.evaluateWindow(window,player,otherPlayer)
# Checks Vertical
for column in range(columnCount):
for column in range(COLUMNCOUNT):
columnArray = [int(i[column]) for i in list(board)]
for place in range(rowCount-3):
for place in range(ROWCOUNT-3):
window = columnArray[place:place+4]
score += self.evaluateWindow(window,player,otherPlayer)
# Checks right diagonal
for row in range(rowCount-3):
for place in range(columnCount-3):
for row in range(ROWCOUNT-3):
for place in range(COLUMNCOUNT-3):
window = [board[row][place],board[row+1][place+1],board[row+2][place+2],board[row+3][place+3]]
score += self.evaluateWindow(window,player,otherPlayer)
for place in range(3,columnCount):
for place in range(3,COLUMNCOUNT):
window = [board[row][place],board[row+1][place-1],board[row+2][place-2],board[row+3][place-3]]
score += self.evaluateWindow(window,player,otherPlayer)
@ -299,20 +388,19 @@ class connectFour():
## Add points if AI wins
#if won == player:
# score += AIScores["win"]
# score += AISCORES["win"]
return score
def evaluateWindow(self, window,player,otherPlayer):
if window.count(player) == 4:
return AIScores["win"]
return AISCORES["win"]
elif window.count(player) == 3 and window.count(0) == 1:
return AIScores["three in a row"]
return AISCORES["three in a row"]
elif window.count(player) == 2 and window.count(0) == 2:
return AIScores["two in a row"]
return AISCORES["two in a row"]
elif window.count(otherPlayer) == 4:
return AIScores["enemy win"]
return AISCORES["enemy win"]
else:
return 0
@ -325,24 +413,24 @@ class connectFour():
return points
if maximizingPlayer:
value = -math.inf
for column in range(0,columnCount):
for column in range(0,COLUMNCOUNT):
testBoard = copy.deepcopy(board)
testBoard = self.placeOnBoard(testBoard,player,column)
if testBoard != None:
evaluation = await self.minimax(testBoard,depth-1,player%2+1,originalPlayer,alpha,beta,False)
if evaluation < -9000: evaluation += AIScores["avoid losing"]
if evaluation < -9000: evaluation += AISCORES["avoid losing"]
value = max(value,evaluation)
alpha = max(alpha,evaluation)
if beta <= alpha: break
return value
else:
value = math.inf
for column in range(0,columnCount):
for column in range(0,COLUMNCOUNT):
testBoard = copy.deepcopy(board)
testBoard = self.placeOnBoard(testBoard,player,column)
if testBoard != None:
evaluation = await self.minimax(testBoard,depth-1,player%2+1,originalPlayer,alpha,beta,True)
if evaluation < -9000: evaluation += AIScores["avoid losing"]
if evaluation < -9000: evaluation += AISCORES["avoid losing"]
value = min(value,evaluation)
beta = min(beta,evaluation)
if beta <= alpha: break

View File

@ -1,172 +0,0 @@
import asyncio
import discord
class GameLoops():
def __init__(self,bot):
self.bot = bot
# Deletes a message
async def deleteMessage(self, imageLocation,channel):
try:
with open("resources/games/oldImages/"+imageLocation, "r") as f:
messages = f.read().splitlines()
for message in messages:
oldMessage = await channel.fetch_message(int(message))
self.bot.log("Deleting old message")
await oldMessage.delete()
except:
oldMessage = ""
return oldMessage
# Runs connect four
async def connectFour(self, ctx, command, user = None, channelId = None):
if user is None:
user = "#"+str(ctx.author.id)
if channelId is None:
channelId = str(ctx.channel_id)
response, showImage, deleteImage, gameDone, gwendoTurn = self.bot.games.connectFour.parseconnectFour(command,channelId, user)
if hasattr(ctx, "send"):
await ctx.send(response)
else:
await ctx.channel.send(response)
self.bot.log(response, channelId)
if showImage:
if deleteImage:
oldImage = await self.deleteMessage("connectFour"+channelId,ctx.channel)
oldImage = await ctx.channel.send(file = discord.File("resources/games/connect4Boards/board"+channelId+".png"))
if gameDone == False:
if gwendoTurn:
response, showImage, deleteImage, gameDone, gwendoTurn = await self.bot.games.connectFour.connectFourAI(channelId)
await ctx.channel.send(response)
self.bot.log(response,channelId)
if showImage:
if deleteImage:
await oldImage.delete()
oldImage = await ctx.channel.send(file = discord.File("resources/games/connect4Boards/board"+channelId+".png"))
if gameDone == False:
with open("resources/games/oldImages/connectFour"+channelId, "w") as f:
f.write(str(oldImage.id))
try:
reactions = ["1","2","3","4","5","6","7"]
for reaction in reactions:
await oldImage.add_reaction(reaction)
except:
self.bot.log("Image deleted before I could react to all of them")
else:
with open("resources/games/oldImages/connectFour"+channelId, "w") as f:
f.write(str(oldImage.id))
try:
reactions = ["1","2","3","4","5","6","7"]
for reaction in reactions:
await oldImage.add_reaction(reaction)
except:
self.bot.log("Image deleted before I could react to all of them")
if gameDone:
game = self.bot.database["connect 4 games"].find_one({"_id":channelId})
try:
with open("resources/games/oldImages/connectFour"+channelId, "r") as f:
oldImage = await channel.fetch_message(int(f.read()))
await oldImage.delete()
except:
self.bot.log("The old image was already deleted")
winner = game["winner"]
difficulty = int(game["difficulty"])
reward = difficulty**2 + 5
if winner != 0:
if game["players"][winner-1].lower() != "gwendolyn":
self.bot.money.addMoney(game["players"][winner-1].lower(),reward)
self.bot.databaseFuncs.deleteGame("connect 4 games",channelId)
async def runHangman(self,channel,user,command = "start", ctx = None):
try:
response, showImage, deleteImage, remainingLetters = self.bot.games.hangman.parseHangman(str(channel.id),user,command)
except:
self.bot.log("Error parsing command (error code 1701)")
if response != "":
if ctx is None:
await channel.send(response)
else:
await ctx.send(response)
self.bot.log(response,str(channel.id))
if showImage:
if deleteImage:
await self.deleteMessage("hangman"+str(channel.id),channel)
oldImage = await channel.send(file = discord.File("resources/games/hangmanBoards/hangmanBoard"+str(channel.id)+".png"))
if len(remainingLetters) > 15:
otherMessage = await channel.send("_ _")
reactionMessages = {oldImage : remainingLetters[:15],otherMessage : remainingLetters[15:]}
else:
otherMessage = ""
reactionMessages = {oldImage : remainingLetters}
oldMessages = str(oldImage.id)
if otherMessage != "":
oldMessages += "\n"+str(otherMessage.id)
with open("resources/games/oldImages/hangman"+str(channel.id), "w") as f:
f.write(oldMessages)
try:
for message, letters in reactionMessages.items():
for letter in letters:
emoji = chr(ord(letter)+127397)
await message.add_reaction(emoji)
except:
self.bot.log("Image deleted before adding all reactions")
# Runs Hex
async def runHex(self,ctx,command,user):
channelId = ctx.channel_id
try:
response, showImage, deleteImage, gameDone, gwendoTurn = self.bot.games.hex.parseHex(command,str(channelId),user)
except:
self.bot.log("Error parsing command (error code 1510)")
await ctx.send(response)
self.bot.log(response,str(channelId))
if showImage:
if deleteImage:
try:
oldImage = await self.deleteMessage("hex"+str(channelId),ctx.channel)
except:
self.bot.log("Error deleting old image (error code 1501)")
oldImage = await ctx.channel.send(file = discord.File("resources/games/hexBoards/board"+str(channelId)+".png"))
if gwendoTurn and not gameDone:
try:
response, showImage, deleteImage, gameDone, gwendoTurn = self.bot.games.hex.hexAI(str(channelId))
except:
response, showImage, deleteImage, gameDone, gwendoTurn = "An AI error ocurred",False,False,False,False
self.bot.log("AI error (error code 1520)")
await ctx.channel.send(response)
self.bot.log(response,str(channelId))
if showImage:
if deleteImage:
await oldImage.delete()
oldImage = await ctx.channel.send(file = discord.File("resources/games/hexBoards/board"+str(channelId)+".png"))
if not gameDone:
with open("resources/games/oldImages/hex"+str(channelId), "w") as f:
f.write(str(oldImage.id))
if gameDone:
game = self.bot.database["hex games"].find_one({"_id":str(channelId)})
winner = game["winner"]
if winner != 0 and game["players"][0] != game["players"][1]: # player1 != player2
winnings = game["difficulty"]*10
self.bot.money.addMoney(game["players"][winner-1].lower(),winnings)
self.bot.databaseFuncs.deleteGame("hex games",str(channelId))

View File

@ -1,8 +1,7 @@
from .invest import Invest
from .trivia import Trivia
from .blackjack import Blackjack
from .connectFour import connectFour
from .gameLoops import GameLoops
from .connectFour import ConnectFour
from .hangman import Hangman
from .hex import HexGame
@ -13,7 +12,6 @@ class Games():
self.invest = Invest(bot)
self.trivia = Trivia(bot)
self.blackjack = Blackjack(bot)
self.connectFour = connectFour(bot)
self.gameLoops = GameLoops(bot)
self.connectFour = ConnectFour(bot)
self.hangman = Hangman(bot)
self.hex = HexGame(bot)

View File

@ -1,4 +1,4 @@
import json, urllib, datetime, string
import json, urllib, datetime, string, discord
from .hangmanDraw import DrawHangman
@ -9,8 +9,13 @@ class Hangman():
self.bot = bot
self.draw = DrawHangman(bot)
def hangmanStart(self,channel,user):
async def start(self, ctx):
await ctx.defer()
channel = str(ctx.channel_id)
user = f"#{ctx.author.id}"
game = self.bot.database["hangman games"].find_one({"_id":channel})
userName = self.bot.databaseFuncs.getName(user)
startedGame = False
if game == None:
apiKey = self.bot.credentials.wordnikKey
@ -26,86 +31,135 @@ class Hangman():
remainingLetters = list(string.ascii_uppercase)
try:
self.draw.drawImage(channel)
except:
self.bot.log("Error drawing image (error code 1710)")
return f"{self.bot.databaseFuncs.getName(user)} started game of hangman.", True, False, remainingLetters
self.draw.drawImage(channel)
logMessage = "Game started"
sendMessage = f"{userName} started game of hangman."
startedGame = True
else:
return "There's already a Hangman game going on in the channel", False, False, []
logMessage = "There was already a game going on"
sendMessage = "There's already a Hangman game going on in the channel"
def hangmanStop(self,channel):
self.bot.database["hangman games"].delete_one({"_id":channel})
self.bot.log(logMessage)
await ctx.send(sendMessage)
return "Game stopped.", False, False, []
if startedGame:
filePath = f"resources/games/hangmanBoards/hangmanBoard{channel}.png"
newImage = await ctx.channel.send(file = discord.File(filePath))
def hangmanGuess(self,channel,user,guess):
blankMessage = await ctx.channel.send("_ _")
reactionMessages = {newImage : remainingLetters[:15], blankMessage : remainingLetters[15:]}
oldMessages = f"{newImage.id}\n{blankMessage.id}"
with open(f"resources/games/oldImages/hangman{channel}", "w") as f:
f.write(oldMessages)
for message, letters in reactionMessages.items():
for letter in letters:
emoji = chr(ord(letter)+127397)
await message.add_reaction(emoji)
async def stop(self, ctx):
channel = str(ctx.channel.id)
game = self.bot.database["hangman games"].find_one({"_id": channel})
if game is None:
await ctx.send("There's no game going on")
elif f"#{ctx.author.id}" != game["player"]:
await ctx.send("You can't end a game you're not in")
else:
self.bot.database["hangman games"].delete_one({"_id":channel})
with open(f"resources/games/oldImages/hangman{channel}", "r") as f:
messages = f.read().splitlines()
for message in messages:
oldMessage = await ctx.channel.fetch_message(int(message))
self.bot.log("Deleting old message")
await oldMessage.delete()
await ctx.send("Game stopped")
async def guess(self, message, user, guess):
channel = str(message.channel.id)
game = self.bot.database["hangman games"].find_one({"_id":channel})
if game != None:
if user == game["player"]:
if len(guess) == 1 and guess in list(string.ascii_uppercase):
if guess not in game["guessed letters"]:
correctGuess = 0
gameExists = (game != None)
singleLetter = (len(guess) == 1 and guess.isalpha())
newGuess = (guess not in game["guessed letters"])
validGuess = (gameExists and singleLetter and newGuess)
for x, letter in enumerate(game["word"]):
if guess == letter:
correctGuess += 1
self.bot.database["hangman games"].update_one({"_id":channel},{"$set":{"guessed."+str(x):True}})
if validGuess:
self.bot.log("Guessed the letter")
correctGuess = 0
if correctGuess == 0:
self.bot.database["hangman games"].update_one({"_id":channel},{"$inc":{"misses":1}})
for x, letter in enumerate(game["word"]):
if guess == letter:
correctGuess += 1
self.bot.database["hangman games"].update_one({"_id":channel},{"$set":{"guessed."+str(x):True}})
self.bot.database["hangman games"].update_one({"_id":channel},{"$push":{"guessed letters":guess}})
if correctGuess == 0:
self.bot.database["hangman games"].update_one({"_id":channel},{"$inc":{"misses":1}})
remainingLetters = list(string.ascii_uppercase)
self.bot.database["hangman games"].update_one({"_id":channel},{"$push":{"guessed letters":guess}})
game = self.bot.database["hangman games"].find_one({"_id":channel})
remainingLetters = list(string.ascii_uppercase)
for letter in game["guessed letters"]:
remainingLetters.remove(letter)
game = self.bot.database["hangman games"].find_one({"_id":channel})
if correctGuess == 1:
message = f"Guessed {guess}. There was 1 {guess} in the word."
else:
message = f"Guessed {guess}. There were {correctGuess} {guess}s in the word."
for letter in game["guessed letters"]:
remainingLetters.remove(letter)
try:
self.draw.drawImage(channel)
except:
self.bot.log("Error drawing image (error code 1710)")
if game["misses"] == 6:
self.hangmanStop(channel)
return message+" You've guessed wrong six times and have lost the game.", True, True, []
elif all(i == True for i in game["guessed"]):
self.hangmanStop(channel)
self.bot.money.addMoney(user,15)
return message+" You've guessed the word! Congratulations! Adding 15 GwendoBucks to your account", True, True, []
else:
return message, True, True, remainingLetters
else:
return f"You've already guessed {guess}", False, False, []
else:
return "", False, False, []
if correctGuess == 1:
sendMessage = f"Guessed {guess}. There was 1 {guess} in the word."
else:
return "", False, False, []
else:
return "There's no Hangman game going on in this channel", False, False, []
sendMessage = f"Guessed {guess}. There were {correctGuess} {guess}s in the word."
self.draw.drawImage(channel)
if game["misses"] == 6:
self.bot.database["hangman games"].delete_one({"_id":channel})
sendMessage += " You've guessed wrong six times and have lost the game."
remainingLetters = []
elif all(i == True for i in game["guessed"]):
self.bot.database["hangman games"].delete_one({"_id":channel})
self.bot.money.addMoney(user,15)
sendMessage += " You've guessed the word! Congratulations! Adding 15 GwendoBucks to your account"
remainingLetters = []
await message.channel.send(sendMessage)
with open(f"resources/games/oldImages/hangman{channel}", "r") as f:
oldMessageIDs = f.read().splitlines()
for oldID in oldMessageIDs:
oldMessage = await message.channel.fetch_message(int(oldID))
self.bot.log("Deleting old message")
await oldMessage.delete()
filePath = f"resources/games/hangmanBoards/hangmanBoard{channel}.png"
newImage = await message.channel.send(file = discord.File(filePath))
if len(remainingLetters) > 0:
if len(remainingLetters) > 15:
blankMessage = await message.channel.send("_ _")
reactionMessages = {newImage : remainingLetters[:15], blankMessage : remainingLetters[15:]}
else:
blankMessage = ""
reactionMessages = {newImage : remainingLetters}
if blankMessage != "":
oldMessages = f"{newImage.id}\n{blankMessage.id}"
else:
oldMessages = str(newImage.id)
with open(f"resources/games/oldImages/hangman{channel}", "w") as f:
f.write(oldMessages)
for message, letters in reactionMessages.items():
for letter in letters:
emoji = chr(ord(letter)+127397)
await message.add_reaction(emoji)
def parseHangman(self,channel,user,command):
if command == "start":
try:
return self.hangmanStart(channel,user)
except:
self.bot.log("Error starting game (error code 1730)")
elif command == "stop":
return self.hangmanStop(channel)
elif command.startswith("guess "):
guess = command[6:].upper()
try:
return self.hangmanGuess(channel,user,guess)
except:
self.bot.log("Error in guessing (Error Code 1720)")
else:
return "I didn't understand that", False, False, []

View File

@ -1,6 +1,7 @@
import random
import copy
import math
import discord
from .hexDraw import DrawHex
@ -13,102 +14,146 @@ for position in ALL_POSITIONS:
HEX_DIRECTIONS = [(0,1),(-1,1),(-1,0),(0,-1),(1,-1),(1,0)]
class HexGame():
def __init__(self,bot):
def __init__(self, bot):
self.bot = bot
self.draw = DrawHex(bot)
# Parses command
def parseHex(self, command, channel, user):
commands = command.lower().split()
async def surrender(self, ctx):
channel = str(ctx.channel_id)
game = self.bot.database["hex games"].find_one({"_id":channel})
if command == "" or command == " ":
return "I didn't get that. Use \"/hex start [opponent]\" to start a game.", False, False, False, False
elif commands[0] == "start":
# Starting a game
if len(commands) == 1: # if the commands is "/hex start", the opponent is Gwendolyn at difficulty 2
commands.append("2")
self.bot.log("Starting a hex game with hexStart(). "+str(user)+" challenged "+commands[1])
return self.hexStart(channel,user,commands[1]) # commands[1] is the opponent
# If using a command with no game, return error
elif game == None:
return "There's no game in this channel", False, False, False, False
# Stopping the game
elif commands[0] == "stop":
if user in game["players"]:
return "Ending game.", False, False, True, False
else:
return "You can't end a game where you're not a player.", False, False, False, False
# Placing a piece
elif commands[0] == "place":
try:
return self.placeHex(channel,commands[1], user)
except:
return "I didn't get that. To place a piece use \"/hex place [position]\". A valid position is e.g. \"E2\".", False, False, False, False
# Undo
elif commands[0] == "undo":
return self.undoHex(channel, user)
# Surrender
elif commands[0] == "surrender":
players = game["players"]
if user in players:
opponent = (players.index(user) + 1) % 2
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"winner":opponent + 1}})
return "{} surrendered. That means {} won! Adding 30 Gwendobucks to their account.".format(self.bot.databaseFuncs.getName(user),self.bot.databaseFuncs.getName(players[opponent])), False, False, True, False
else:
return "You can't surrender when you're not a player.", False, False, False, False
# Swap
elif commands[0] == "swap":
if len(game["gameHistory"]) == 1: # Only after the first move
self.bot.database["hex games"].update_one({"_id":channel},
{"$set":{"players":game["players"][::-1]}}) # Swaps their player-number
# Swaps the color of the hexes on the board drawing:
self.draw.drawSwap(channel)
player2 = game["players"][1]
gwendoTurn = (player2 == "Gwendolyn")
return "The color of both players were swapped. It is now {}'s turn".format(player2), True, True, False, gwendoTurn
else:
return "You can only swap as the second player after the very first move.", False, False, False, False
user = f"#{ctx.author.id}"
players = game["players"]
if user not in players:
await ctx.send("You can't surrender when you're not a player.")
else:
return "I didn't get that. Use \"/hex start [opponent]\" to start a game, \"/hex place [position]\" to place a piece, \"/hex undo\" to undo your last move or \"/hex stop\" to stop a current game.", False, False, False, False
opponent = (players.index(user) + 1) % 2
opponentName = self.bot.databaseFuncs.getName(players[opponent])
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"winner":opponent + 1}})
await ctx.send(f"{ctx.author.display_name} surrendered. That means {opponentName} won! Adding 30 Gwendobucks to their account")
with open(f"resources/games/oldImages/hex{channel}", "r") as f:
oldImage = await ctx.channel.fetch_message(int(f.read()))
if oldImage is not None:
await oldImage.delete()
else:
self.bot.log("The old image was already deleted")
self.bot.log("Sending the image")
filePath = f"resources/games/hexBoards/board{channel}.png"
oldImage = await ctx.channel.send(file = discord.File(filePath))
with open(f"resources/games/oldImages/hex{channel}", "w") as f:
f.write(str(oldImage.id))
# Swap
async def swap(self, ctx):
channel = str(ctx.channel_id)
game = self.bot.database["hex games"].find_one({"_id":channel})
user = f"#{ctx.author.id}"
if game is None:
await ctx.send("You can't swap nothing")
elif user not in game["players"]:
await ctx.send("You're not in the game")
elif len(game["gameHistory"]) != 1: # Only after the first move
await ctx.send("You can only swap as the second player after the very first move.")
elif user != game["players"][game["turn"]-1]:
await ctx.send("You can only swap after your opponent has placed their piece")
else:
self.bot.database["hex games"].update_one({"_id":channel},
{"$set":{"players":game["players"][::-1]}}) # Swaps their player-number
# Swaps the color of the hexes on the board drawing:
self.draw.drawSwap(channel)
opponent = game["players"][::-1][game["turn"]-1]
gwendoTurn = (opponent == f"#{self.bot.user.id}")
opponentName = self.bot.databaseFuncs.getName(opponent)
await ctx.send(f"The color of the players were swapped. It is now {opponentName}'s turn")
with open(f"resources/games/oldImages/hex{channel}", "r") as f:
oldImage = await ctx.channel.fetch_message(int(f.read()))
if oldImage is not None:
await oldImage.delete()
else:
self.bot.log("The old image was already deleted")
self.bot.log("Sending the image")
filePath = f"resources/games/hexBoards/board{channel}.png"
oldImage = await ctx.channel.send(file = discord.File(filePath))
with open(f"resources/games/oldImages/hex{channel}", "w") as f:
f.write(str(oldImage.id))
if gwendoTurn:
await self.hexAI(ctx)
# Starts the game
def hexStart(self, channel, user, opponent):
async def start(self, ctx, opponent):
try:
await ctx.defer()
except:
self.bot.log("Defer failed")
user = f"#{ctx.author.id}"
channel = str(ctx.channel_id)
game = self.bot.database["hex games"].find_one({"_id":channel})
if game == None:
if opponent in ["1","2","3","4","5"]:
difficulty = int(opponent)
diffText = " with difficulty "+opponent
opponent = "Gwendolyn"
elif opponent.lower() == "gwendolyn":
difficulty = 2
diffText = " with difficulty 2"
opponent = "Gwendolyn"
else:
try:
int(opponent)
return "That difficulty doesn't exist", False, False, False, False
except:
opponent = self.bot.databaseFuncs.getID(opponent)
if opponent == None:
return "I can't find that user", False, False, False, False
else:
# Opponent is another player
difficulty = 3
diffText = ""
startedGame = False
canStart = True
if game != None:
sendMessage = "There's already a hex game going on in this channel"
logMessage = "There was already a game going on"
canStart = False
else:
if type(opponent) == int:
# Opponent is Gwendolyn
if opponent in range(1, 6):
opponentName = "Gwendolyn"
difficulty = int(opponent)
diffText = f" with difficulty {difficulty}"
opponent = f"#{self.bot.user.id}"
else:
sendMessage = "Difficulty doesn't exist"
logMessage = "They tried to play against a difficulty that doesn't exist"
canStart = False
elif type(opponent) == discord.member.Member:
if opponent.bot:
# User has challenged a bot
if opponent == self.bot.user:
# It was Gwendolyn
opponentName = "Gwendolyn"
difficulty = 2
diffText = f" with difficulty {difficulty}"
opponent = f"#{self.bot.user.id}"
else:
sendMessage = "You can't challenge a bot!"
logMessage = "They tried to challenge a bot"
canStart = False
else:
# Opponent is another player
if ctx.author != opponent:
opponentName = opponent.display_name
opponent = f"#{opponent.id}"
difficulty = 5
diffText = ""
else:
sendMessage = "You can't play against yourself"
logMessage = "They tried to play against themself"
canStart = False
else:
canStart = False
logMessage = f"Opponent was neither int or member. It was {type(opponent)}"
sendMessage = "Something went wrong"
if canStart:
# board is 11x11
board = [ [ 0 for i in range(BOARDWIDTH) ] for j in range(BOARDWIDTH) ]
players = [user,opponent]
board = [[0 for i in range(BOARDWIDTH)] for j in range(BOARDWIDTH)]
players = [user, opponent]
random.shuffle(players) # random starting player
gameHistory = []
@ -120,137 +165,196 @@ class HexGame():
# draw the board
self.draw.drawBoard(channel)
gwendoTurn = True if players[0] == "Gwendolyn" else False
showImage = True
return "Started Hex game against "+self.bot.databaseFuncs.getName(opponent)+ diffText+". It's "+self.bot.databaseFuncs.getName(players[0])+"'s turn", showImage, False, False, gwendoTurn
else:
return "There's already a hex game going on in this channel", False, False, False, False
gwendoTurn = (players[0] == f"#{self.bot.user.id}")
startedGame = True
turnName = self.bot.databaseFuncs.getName(players[0])
sendMessage = f"Started Hex game against {opponentName}{diffText}. It's {turnName}'s turn"
logMessage = "Game started"
await ctx.send(sendMessage)
self.bot.log(logMessage)
if startedGame:
filePath = f"resources/games/hexBoards/board{ctx.channel_id}.png"
newImage = await ctx.channel.send(file = discord.File(filePath))
with open(f"resources/games/oldImages/hex{ctx.channel_id}", "w") as f:
f.write(str(newImage.id))
if gwendoTurn:
await self.hexAI(ctx)
# Places a piece at the given location and checks things afterwards
def placeHex(self, channel : str,position : str, user):
async def placeHex(self, ctx, position : str, user):
channel = str(ctx.channel_id)
game = self.bot.database["hex games"].find_one({"_id":channel})
placedPiece = False
if game != None:
players = game["players"]
if user in players:
turn = game["turn"]
if players[0] == players[1]:
player = turn
else:
player = players.index(user)+1
if player == turn:
board = game["board"]
self.bot.log("Placing a piece on the board with placeHex()")
# Places on board
board = self.placeOnHexBoard(board,player,position)
if isinstance(board, list):
# If the move is valid:
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"board":board}})
turn = 1 if turn == 2 else 2
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"turn":turn}})
# Checking for a win
self.bot.log("Checking for win")
winner = self.evaluateBoard(game["board"])[1]
if winner == 0: # Continue with the game.
gameWon = False
message = self.bot.databaseFuncs.getName(game["players"][player-1])+" placed at "+position.upper()+". It's now "+self.bot.databaseFuncs.getName(game["players"][turn-1])+"'s turn."# The score is "+str(score)
else: # Congratulations!
gameWon = True
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"winner":winner}})
message = self.bot.databaseFuncs.getName(game["players"][player-1])+" placed at "+position.upper()+" and won!"
if game["players"][winner-1] != "Gwendolyn":
winAmount = game["difficulty"]*10
message += " Adding "+str(winAmount)+" GwendoBucks to their account."
self.bot.database["hex games"].update_one({"_id":channel},
{"$push":{"gameHistory":(int(position[1])-1, ord(position[0])-97)}})
# Is it now Gwendolyn's turn?
gwendoTurn = False
if game["players"][turn-1] == "Gwendolyn":
self.bot.log("It's Gwendolyn's turn")
gwendoTurn = True
# Update the board
self.draw.drawHexPlacement(channel,player,position)
return message, True, True, gameWon, gwendoTurn
else:
# Invalid move. "board" is the error message
message = board
return message, False, False, False, False
else:
# Move out of turn
message = "It isn't your turn, it is "+self.bot.databaseFuncs.getName(game["players"][turn-1])+"'s turn."
return message, False, False, False, False
else:
message = "You can't place when you're not in the game. The game's players are: "+self.bot.databaseFuncs.getName(game["players"][0])+" and "+self.bot.databaseFuncs.getName(game["players"][1])+"."
return message, False, False, False, False
if game == None:
sendMessage = "There's no game in this channel"
self.bot.log("There was no game going on")
elif not (position[0].isalpha() and position[1:].isnumeric() and len(position) in [2, 3]):
sendMessage = "The position must be a letter followed by a number."
self.bot.log(f"The position was not valid, {position}")
else:
return "There's no game in this channel", False, False, False, False
players = game["players"]
if user not in players:
sendMessage = f"You can't place when you're not in the game. The game's players are: {self.bot.databaseFuncs.getName(game['players'][0])} and {self.bot.databaseFuncs.getName(game['players'][1])}."
self.bot.log("They aren't in the game")
elif players[game["turn"]-1] != user:
sendMessage = "It's not your turn"
self.bot.log("It wasn't their turn")
else:
player = game["turn"]
turn = game["turn"]
board = game["board"]
self.bot.log("Placing a piece on the board with placeHex()")
# Places on board
board = self.placeOnHexBoard(board,player,position)
if board is None:
self.bot.log("It was an invalid position")
sendMessage = ("That's an invalid position. You must place your piece on an empty field.")
else:
# If the move is valid:
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"board":board}})
turn = (turn % 2) + 1
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"turn":turn}})
# Checking for a win
self.bot.log("Checking for win")
winner = self.evaluateBoard(game["board"])[1]
if winner == 0: # Continue with the game.
gameWon = False
sendMessage = self.bot.databaseFuncs.getName(game["players"][player-1])+" placed at "+position.upper()+". It's now "+self.bot.databaseFuncs.getName(game["players"][turn-1])+"'s turn."# The score is "+str(score)
else: # Congratulations!
gameWon = True
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"winner":winner}})
sendMessage = self.bot.databaseFuncs.getName(game["players"][player-1])+" placed at "+position.upper()+" and won!"
if game["players"][winner-1] != f"#{self.bot.user.id}":
winAmount = game["difficulty"]*10
sendMessage += " Adding "+str(winAmount)+" GwendoBucks to their account."
self.bot.database["hex games"].update_one({"_id":channel},
{"$push":{"gameHistory":(int(position[1])-1, ord(position[0])-97)}})
# Is it now Gwendolyn's turn?
gwendoTurn = False
if game["players"][turn-1] == f"#{self.bot.user.id}":
self.bot.log("It's Gwendolyn's turn")
gwendoTurn = True
placedPiece = True
if user == f"#{self.bot.user.id}":
await ctx.channel.send(sendMessage)
else:
await ctx.send(sendMessage)
if placedPiece:
# Update the board
self.draw.drawHexPlacement(channel,player, position)
with open(f"resources/games/oldImages/hex{channel}", "r") as f:
oldImage = await ctx.channel.fetch_message(int(f.read()))
if oldImage is not None:
await oldImage.delete()
else:
self.bot.log("The old image was already deleted")
self.bot.log("Sending the image")
filePath = f"resources/games/hexBoards/board{channel}.png"
oldImage = await ctx.channel.send(file = discord.File(filePath))
if gameWon:
self.bot.log("Dealing with the winning player")
game = self.bot.database["hex games"].find_one({"_id":channel})
winner = game["winner"]
if game["players"][winner-1] != f"#{self.bot.user.id}":
winnings = game["difficulty"]*10
self.bot.money.addMoney(game["players"][winner-1].lower(),winnings)
else:
with open(f"resources/games/oldImages/hex{channel}", "w") as f:
f.write(str(oldImage.id))
if gwendoTurn:
await self.hexAI(ctx)
# Returns a board where the placement has ocurred
def placeOnHexBoard(self, board,player,position):
# Translates the position
position = position.lower()
# Error handling
try:
column = ord(position[0]) - 97 # ord() translates from letter to number
row = int(position[1:]) - 1
if column not in range(BOARDWIDTH) or row not in range(BOARDWIDTH):
self.bot.log("Position out of bounds (error code 1533)")
return "Error. That position is out of bounds."
except:
self.bot.log("Invalid position (error code 1531)")
return "Error. The position should be a letter followed by a number, e.g. \"e2\"."
column = ord(position[0]) - 97 # ord() translates from letter to number
row = int(position[1:]) - 1
if column not in range(BOARDWIDTH) or row not in range(BOARDWIDTH):
self.bot.log("Position out of bounds")
return None
# Place at the position
if board[row][column] == 0:
board[row][column] = player
return board
else:
self.bot.log("Cannot place on existing piece (error code 1532)")
return "Error. You must place on an empty space."
return None
# After your move, you have the option to undo get your turn back #TimeTravel
def undoHex(self, channel, user):
async def undo(self, ctx):
channel = str(ctx.channel_id)
user = f"#{ctx.author.id}"
undid = False
game = self.bot.database["hex games"].find_one({"_id":channel})
if user in game["players"]:
if len(game["gameHistory"]):
turn = game["turn"]
# You can only undo after your turn, which is the opponent's turn.
if user == game["players"][(turn % 2)]: # If it's not your turn
self.bot.log("Undoing {}'s last move".format(self.bot.databaseFuncs.getName(user)))
lastMove = game["gameHistory"].pop()
game["board"][lastMove[0]][lastMove[1]] = 0
self.bot.database["hex games"].update_one({"_id":channel},
{"$set":{"board":game["board"]}})
self.bot.database["hex games"].update_one({"_id":channel},
{"$set":{"turn":turn%2 + 1}})
# Update the board
self.draw.drawHexPlacement(channel,0,"abcdefghijk"[lastMove[1]]+str(lastMove[0]+1)) # The zero makes the hex disappear
return "You undid your last move at {}".format(lastMove), True, True, False, False
else:
# Sassy
return "Nice try. You can't undo your opponent's move. ", False, False, False, False
else:
# Sassy
return "Really? You undo right at the start of the game?", False, False, False, False
if user not in game["players"]:
sendMessage = "You're not a player in the game"
elif len(game["gameHistory"]) == 0:
sendMessage = "You can't undo nothing"
elif user != game["players"][(game["turn"] % 2)]: # If it's not your turn
sendMessage = "It's not your turn"
else:
return "You're not a player in the game", False, False, False, False
turn = game["turn"]
self.bot.log("Undoing {}'s last move".format(self.bot.databaseFuncs.getName(user)))
lastMove = game["gameHistory"].pop()
game["board"][lastMove[0]][lastMove[1]] = 0
self.bot.database["hex games"].update_one({"_id":channel},
{"$set":{"board":game["board"]}})
self.bot.database["hex games"].update_one({"_id":channel},
{"$set":{"turn":turn%2 + 1}})
# Update the board
self.draw.drawHexPlacement(channel,0,"abcdefghijk"[lastMove[1]]+str(lastMove[0]+1)) # The zero makes the hex disappear
sendMessage = f"You undid your last move at {lastMove}"
undid = True
await ctx.send(sendMessage)
if undid:
with open(f"resources/games/oldImages/hex{channel}", "r") as f:
oldImage = await ctx.channel.fetch_message(int(f.read()))
if oldImage is not None:
await oldImage.delete()
else:
self.bot.log("The old image was already deleted")
self.bot.log("Sending the image")
filePath = f"resources/games/hexBoards/board{channel}.png"
oldImage = await ctx.channel.send(file = discord.File(filePath))
with open(f"resources/games/oldImages/hex{channel}", "w") as f:
f.write(str(oldImage.id))
# Plays as the AI
def hexAI(self, channel):
async def hexAI(self, ctx):
channel = str(ctx.channel_id)
self.bot.log("Figuring out best move")
game = self.bot.database["hex games"].find_one({"_id":channel})
board = game["board"]
@ -269,32 +373,10 @@ class HexGame():
moves.remove(move)
chosenMove = random.choice(moves)
"""
GwenColor = data[channel]["players"].index("Gwendolyn") + 1 # either 1 or 2 - red or blue
if len(data[channel]["gameHistory"]) == 0:
return placeHex(channel,"F6", "Gwendolyn") # If starting, start in the middle
board = data[channel]["board"]
difficulty = data[channel]["difficulty"]
possiblePlaces = [i for i,v in enumerate(sum(board,[])) if v == 0]
judgements = [float('nan')]*len(possiblePlaces) # All possible moves are yet to be judged
current_score = evaluateBoard(board)[0]
for i in possiblePlaces:
testBoard = copy.deepcopy(board)
testBoard[i // BOARDWIDTH][i % BOARDWIDTH] = GwenColor
if evaluateBoard(testBoard)[0] != current_score: # only think about a move if it improves the score (it's impossible to get worse)
# Testing a move and evaluating it
judgements[i] = minimaxHex(testBoard,difficulty,-math.inf,math.inf,GwenColor==2)
self.bot.log("Best score for place {} is {}".format((i // BOARDWIDTH,i % BOARDWIDTH),judgements[i]))
bestScore = max(judgements) if (GwenColor == 1) else min(judgements) # this line has an error
indices = [i for i, x in enumerate(judgements) if x == bestScore] # which moves got that score?
i = random.choice(indices)
chosenMove = (i // BOARDWIDTH , i % BOARDWIDTH)
"""
placement = "abcdefghijk"[chosenMove[1]]+str(chosenMove[0]+1)
self.bot.log("ChosenMove is {} at {}".format(chosenMove,placement))
return self.placeHex(channel,placement, "Gwendolyn")
self.bot.log(f"ChosenMove is {chosenMove} at {placement}")
await self.placeHex(ctx, placement, f"#{self.bot.user.id}")
def evaluateBoard(self, board):

View File

@ -122,7 +122,7 @@ class DrawHex():
def drawHexPlacement(self, channel,player,position):
FILEPATH = "resources/games/hexBoards/board"+channel+".png"
self.bot.log("Drawing a newly placed hex. Filepath: "+FILEPATH)
self.bot.log(f"Drawing a newly placed hex. Filename: board{channel}.png")
# Translates position
# We don't need to error-check, because the position is already checked in placeOnHexBoard()

View File

@ -1,3 +1,5 @@
import discord
class Invest():
def __init__(self, bot):
self.bot = bot
@ -103,35 +105,48 @@ class Invest():
else:
return "no"
def parseInvest(self, content: str, user : str):
if content.startswith("check"):
commands = content.split(" ")
async def parseInvest(self, ctx, parameters):
try:
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:
return self.getPortfolio(user)
response = self.getPortfolio(user)
else:
price = self.getPrice(commands[1])
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:
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"):
commands = content.split(" ")
elif parameters.startswith("buy"):
commands = parameters.split(" ")
if len(commands) == 3:
#try:
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]\""
response = self.buyStock(user,commands[1],int(commands[2]))
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"):
commands = content.split(" ")
elif parameters.startswith("sell"):
commands = parameters.split(" ")
if len(commands) == 3:
try:
return self.sellStock(user,commands[1],int(commands[2]))
response = self.sellStock(user,commands[1],int(commands[2]))
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:
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"]
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
def addMoney(self,user,amount):
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})
# Transfers money from one user to another
def giveMoney(self,user,targetUser,amount):
userData = self.database["users"].find_one({"_id":user})
targetUser = self.bot.databaseFuncs.getID(targetUser)
async def giveMoney(self, ctx, user, amount):
try:
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 targetUser != None:
if userData != None:
if userData["money"] >= amount:
self.addMoney(user,-1 * amount)
self.addMoney(f"#{ctx.author.id}",-1 * 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:
self.bot.log("They didn't have enough GwendoBucks (error code 1223b)")
return "You don't have that many GwendoBucks (error code 1223b)"
self.bot.log("They didn't have enough GwendoBucks")
await ctx.send("You don't have that many GwendoBucks")
else:
self.bot.log("They didn't have enough GwendoBucks (error code 1223a)")
return "You don't have that many GwendoBucks (error code 1223a)"
self.bot.log("They didn't have enough GwendoBucks")
await ctx.send("You don't have that many GwendoBuck")
else:
self.bot.log("They weren't in the system")
return "The target doesn't exist"
await ctx.send("The target doesn't exist")
else:
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 urllib
import random
import asyncio
class Trivia():
def __init__(self, bot):
@ -83,3 +84,38 @@ class Trivia():
self.bot.log("Couldn't find the question (error code 1102)")
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

@ -1,5 +1,6 @@
import math
import json
import discord
from utils import cap
@ -18,27 +19,28 @@ class LookupFuncs():
return(str(mods))
# Looks up a monster
def monsterFunc(self, command):
self.bot.log("Looking up "+command)
async def monsterFunc(self, ctx, query):
query = cap(query)
self.bot.log("Looking up "+query)
# 1-letter monsters don't exist
if len(command) < 2:
self.bot.log("Monster name too short (error code 601)")
return("I don't know that monster... (error code 601)","","","","","")
if len(query) < 2:
self.bot.log("Monster name too short")
await ctx.send("I don't know that monster...")
else:
# Opens "monsters.json"
data = json.load(open('resources/lookup/monsters.json', encoding = "utf8"))
for monster in data:
if "name" in monster and str(command) == monster["name"]:
if "name" in monster and str(query) == monster["name"]:
self.bot.log("Found it!")
# Looks at the information about the monster and returns that information
# in separate variables, allowing Gwendolyn to know where to separate
# the messages
if monster["subtype"] != "":
typs = (monster["type"]+" ("+monster["subtype"]+")")
types = (monster["type"]+" ("+monster["subtype"]+")")
else:
typs = monster["type"]
types = monster["type"]
con_mod = math.floor((monster["constitution"]-10)/2)
hit_dice = monster["hit_dice"]
@ -80,10 +82,10 @@ class LookupFuncs():
if c_immunities != "":
c_immunities = "\n**Condition Immunities** "+c_immunities
spec_ab = ""
specialAbilities = ""
if "special_abilities" in monster:
for ability in monster["special_abilities"]:
spec_ab += "\n\n***"+ability["name"]+".*** "+ability["desc"]
specialAbilities += "\n\n***"+ability["name"]+".*** "+ability["desc"]
act = ""
if "actions" in monster:
@ -95,10 +97,10 @@ class LookupFuncs():
for reaction in monster["reactions"]:
react += "\n\n***"+reaction["name"]+".*** "+reaction["desc"]
leg_act = ""
legendaryActions = ""
if "legendary_actions" in monster:
for action in monster["legendary_actions"]:
leg_act += "\n\n***"+action["name"]+".*** "+action["desc"]
legendaryActions += "\n\n***"+action["name"]+".*** "+action["desc"]
if con_mod < 0:
hit_dice += (" - "+str(con_mod * int(monster["hit_dice"].replace("d"," ").split()[0])*(-1)))
@ -107,33 +109,56 @@ class LookupFuncs():
new_part = "\n--------------------"
monster_type = monster["size"]+" "+typs+", "+monster["alignment"]+"*"
monster_type = monster["size"]+" "+types+", "+monster["alignment"]+"*"
basic_info = "\n**Armor Class** "+str(monster["armor_class"])+"\n**Hit Points** "+str(monster["hit_points"])+" ("+hit_dice+")\n**Speed **"+monster["speed"]+new_part+"\n"
text1 = (monster_type+new_part+basic_info+stats+new_part+saving_throws+skills+vulnerabilities+resistances+immunities+c_immunities+"\n**Senses** "+monster["senses"]+"\n**Languages** "+monster["languages"]+"\n**Challenge** "+monster["challenge_rating"])
info = (monster_type+new_part+basic_info+stats+new_part+saving_throws+skills+vulnerabilities+resistances+immunities+c_immunities+"\n**Senses** "+monster["senses"]+"\n**Languages** "+monster["languages"]+"\n**Challenge** "+monster["challenge_rating"])
text2 = (spec_ab)
text3 = (act)
text4 = (react)
text5 = (leg_act)
monsterInfo = [(info, query),
(specialAbilities, "Special Abilities"),
(act, "Actions"),
(react, "Reactions"),
(legendaryActions, "Legendary Actions")]
self.bot.log("Returning monster information")
return(str(command),text1,text2,text3,text4,text5)
self.bot.log("Monster not in database (error code 602)")
return("I don't know that monster... (error code 602)","","","","","")
# Sends the received information. Separates into separate messages if
# there is too much text
await ctx.send(f"Result for \"{query}\"")
for text, title in monsterInfo:
if text != "":
if len(text) < 2000:
em = discord.Embed(title = title, description = text, colour=0xDEADBF)
await ctx.channel.send(embed = em)
else:
index = text[:2000].rfind(".")+1
em1 = discord.Embed(title = title, description = text[:index], colour=0xDEADBF)
await ctx.channel.send(embed = em1)
em2 = discord.Embed(title = "", description = text[index+1:], colour=0xDEADBF)
await ctx.channel.send(embed = em2)
break
else:
self.bot.log("Monster not in database")
await ctx.send("I don't know that monster...")
# Looks up a spell
def spellFunc(self, command):
self.bot.log("Looking up "+command)
async def spellFunc(self, ctx, query):
query = cap(query)
self.bot.log("Looking up "+query)
# Opens "spells.json"
data = json.load(open('resources/lookup/spells.json', encoding = "utf8"))
if str(command) in data:
if query in data:
self.bot.log("Returning spell information")
spell_output = ("***"+str(command)+"***\n*"+str(data[str(command)]["level"])+" level "+str(data[str(command)]["school"])+"\nCasting Time: "+str(data[str(command)]["casting_time"])+"\nRange: "+str(data[str(command)]["range"])+"\nComponents: "+str(data[str(command)]["components"])+"\nDuration: "+str(data[str(command)]["duration"])+"*\n \n"+str(data[str(command)]["description"]))
sendMessage = (f"***{query}***\n*{data[query]['level']} level {data[query]['school']}\nCasting Time: {data[query]['casting_time']}\nRange:{data[query]['range']}\nComponents:{data[query]['components']}\nDuration:{data[query]['duration']}*\n \n{data[query]['description']}")
else:
self.bot.log("I don't know that spell (error code 501)")
spell_output = "I don't think that's a spell (error code 501)"
self.bot.log("Successfully ran /spell")
return(spell_output)
sendMessage = "I don't think that's a spell (error code 501)"
if len(sendMessage) > 2000:
await ctx.send(sendMessage[:2000])
await ctx.send(sendMessage[2000:])
else:
await ctx.send(sendMessage)

View File

@ -13,6 +13,8 @@ class BedreNetflix():
#Returns a list of no more than 5 options when user requests a movie
async def requestMovie(self, ctx, movieName):
await ctx.defer()
self.bot.log("Searching for "+movieName)
movieList = imdb.IMDb().search_movie(movieName)
movies = []
@ -89,6 +91,8 @@ class BedreNetflix():
#Returns a list of no more than 5 options when user requests a show
async def requestShow(self, ctx, showName):
await ctx.defer()
self.bot.log("Searching for "+showName)
movies = imdb.IMDb().search_movie(showName) #Replace with tvdb
shows = []
@ -301,6 +305,8 @@ class BedreNetflix():
await ctx.send("```"+messageText[:cutOffIndex]+"```")
await SendLongMessage(ctx,messageText[cutOffIndex+1:])
await ctx.defer()
# showDM, showMovies, showShows, episodes
params = [False, False, False, False]
showDMArgs = ["d", "dm", "downloading", "downloadmanager"]

View File

@ -15,7 +15,7 @@ class Generators():
yield (corpus[i], corpus[i+1], corpus[i+2])
# Generates a random name
def nameGen(self):
async def nameGen(self, ctx):
# Makes a list of all names from "names.txt"
names = open('resources/names.txt', encoding='utf8').read()
corpus = list(names)
@ -74,11 +74,12 @@ class Generators():
done = True
genName = "".join(chain)
self.bot.log("Generated "+genName[:-1])
# Returns the name
return(genName)
await ctx.send(genName)
# Generates a random tavern name
def tavernGen(self):
async def tavernGen(self, ctx):
# Lists first parts, second parts and third parts of tavern names
fp = ["The Silver","The Golden","The Staggering","The Laughing","The Prancing","The Gilded","The Running","The Howling","The Slaughtered","The Leering","The Drunken","The Leaping","The Roaring","The Frowning","The Lonely","The Wandering","The Mysterious","The Barking","The Black","The Gleaming","The Tap-Dancing","The Sad","The Sexy","The Artificial","The Groovy","The Merciful","The Confused","The Pouting","The Horny","The Okay","The Friendly","The Hungry","The Handicapped","The Fire-breathing","The One-Eyed","The Psychotic","The Mad","The Evil","The Idiotic","The Trusty","The Busty"]
sp = ["Eel","Dolphin","Dwarf","Pegasus","Pony","Rose","Stag","Wolf","Lamb","Demon","Goat","Spirit","Horde","Jester","Mountain","Eagle","Satyr","Dog","Spider","Star","Dad","Rat","Jeremy","Mouse","Unicorn","Pearl","Ant","Crab","Penguin","Octopus","Lawyer","Ghost","Toad","Handjob","Immigrant","SJW","Dragon","Bard","Sphinx","Soldier","Salmon","Owlbear","Kite","Frost Giant","Arsonist"]
@ -89,4 +90,4 @@ class Generators():
self.bot.log("Generated "+genTav)
# Return the name
return(genTav)
await ctx.send(genTav)

View File

@ -10,6 +10,8 @@ from .bedreNetflix import BedreNetflix
from .nerdShit import NerdShit
from .generators import Generators
from utils import cap
class MyStringifier(d20.MarkdownStringifier):
def _str_expression(self, node):
if node.comment == None:
@ -57,29 +59,31 @@ class Other():
await ctx.send(embed = embed)
# Responds with a greeting of a time-appropriate maner
def helloFunc(self, author):
async def helloFunc(self, ctx):
def time_in_range(start, end, x):
# Return true if x is in the range [start, end]
if start <= end:
return start <= x <= end
else:
return start <= x or x <= end
author = ctx.author.display_name
now = datetime.datetime.now()
if time_in_range(now.replace(hour=5, minute=0, second=0, microsecond=0),now.replace(hour=10, minute=0, second=0, microsecond=0), now):
return("Good morning, "+str(author))
elif time_in_range(now.replace(hour=10, minute=0, second=0, microsecond=0),now.replace(hour=13, minute=0, second=0, microsecond=0), now):
return("Good day, "+str(author))
sendMessage = "Good morning, "+str(author)
elif time_in_range(now.replace(hour=13, minute=0, second=0, microsecond=0),now.replace(hour=18, minute=0, second=0, microsecond=0), now):
return("Good afternoon, "+str(author))
sendMessage = "Good afternoon, "+str(author)
elif time_in_range(now.replace(hour=18, minute=0, second=0, microsecond=0),now.replace(hour=22, minute=0, second=0, microsecond=0), now):
return("Good evening, "+str(author))
sendMessage = "Good evening, "+str(author)
elif time_in_range(now.replace(hour=22, minute=0, second=0, microsecond=0),now.replace(hour=23, minute=59, second=59, microsecond=0), now):
return("Good night, "+str(author))
sendMessage = "Good night, "+str(author)
else:
return("Hello, "+str(author))
sendMessage = "Hello, "+str(author)
await ctx.send(sendMessage)
# Finds a random picture online
def imageFunc(self):
async def imageFunc(self, ctx):
# Picks a type of camera, which decides the naming scheme
cams = ("one","two","three","four")
cam = random.choice(cams)
@ -113,22 +117,31 @@ class Other():
tree = lxml.etree.HTML(read)
images = tree.xpath('//a[@class = "thumb"]/@href')
# Picks an image
number = random.randint(1,len(images))-1
image = images[number]
if len(images) == 0:
await ctx.send("Found no images")
else:
# Picks an image
number = random.randint(1,len(images))-1
image = images[number]
self.bot.log("Picked image number "+str(number))
self.bot.log("Picked image number "+str(number))
# Returns the image
self.bot.log("Successfully returned an image")
return(image)
# Returns the image
self.bot.log("Successfully returned an image")
await ctx.send(image)
# Finds a page from the Senkulpa Wikia
def findWikiPage(self, search : str):
async def findWikiPage(self, ctx, search : str):
await ctx.defer()
search = cap(search)
foundPage = False
self.bot.log("Trying to find wiki page for "+search)
wikia.set_lang("da")
searchResults = wikia.search("senkulpa",search)
if len(searchResults) > 0:
foundPage = True
searchResult = searchResults[0].replace(",","%2C")
self.bot.log("Found page \""+searchResult+"\"")
page = wikia.page("senkulpa",searchResult)
@ -137,15 +150,39 @@ class Other():
images = page.images
if len(images) > 0:
image = images[len(images)-1]+"/revision/latest?path-prefix=da"
return page.title, content, image
else:
return page.title, content, ""
image = ""
else:
self.bot.log("Couldn't find the page (error code 1002)")
return "", "Couldn't find page (error code 1002)", ""
self.bot.log("Couldn't find the page")
await ctx.send("Couldn't find page (error code 1002)")
def rollDice(self, user, rollString):
if foundPage:
self.bot.log("Sending the embedded message",str(ctx.channel_id))
content += f"\n[Læs mere]({page.url})"
embed = discord.Embed(title = page.title, description = content, colour=0xDEADBF)
if image != "":
embed.set_thumbnail(url=image)
await ctx.send(embed = embed)
async def rollDice(self, ctx, rollString):
user = ctx.author.display_name
while len(rollString) > 1 and rollString[0] == " ":
rollString = rollString[1:]
return user+" :game_die:\n"+str(d20.roll(rollString, allow_comments=True,stringifier=MyStringifier()))
roll = d20.roll(rollString, allow_comments=True, stringifier=MyStringifier())
await ctx.send(f"{user} :game_die:\n{roll}")
async def helpFunc(self, ctx, command):
if command == "":
with open("resources/help/help.txt",encoding="utf-8") as f:
text = f.read()
em = discord.Embed(title = "Help", description = text,colour = 0x59f442)
await ctx.send(embed = em)
else:
self.bot.log(f"Looking for help-{command}.txt",str(ctx.channel_id))
with open(f"resources/help/help-{command}.txt",encoding="utf-8") as f:
text = f.read()
em = discord.Embed(title = command.capitalize(), description = text,colour = 0x59f442)
await ctx.send(embed = em)

View File

@ -1,5 +1,6 @@
import json
import string
import discord
class StarWarsChar():
def __init__(self, bot):
@ -470,7 +471,10 @@ class StarWarsChar():
return cmd
def parseChar(self,user : str, cmd : str):
async def parseChar(self, ctx, parameters : str):
user = f"#{ctx.author.id}"
cmd = string.capwords(parameters.replace("+","+ ").replace("-","- ").replace(",",", "))
returnEmbed = False
cmd = self.replaceSpaces(cmd)
@ -487,8 +491,9 @@ class StarWarsChar():
if cmd == "":
if userCharacter != None:
text1, text2 = self.characterSheet(userCharacter)
return text1, self.replaceWithSpaces(text2)
title, text = self.characterSheet(userCharacter)
text = self.replaceWithSpaces(text)
returnEmbed = True
else:
self.bot.log("Makin' a character for "+self.bot.databaseFuncs.getName(user))
with open("resources/starWars/starwarstemplates.json", "r") as f:
@ -496,14 +501,20 @@ class StarWarsChar():
newChar = templates["Character"]
newChar["_id"] = user
self.bot.database["starwars characters"].insert_one(newChar)
return "", "Character for " + self.bot.databaseFuncs.getName(user) + " created"
await ctx.send("Character for " + self.bot.databaseFuncs.getName(user) + " created")
else:
if cmd == "Purge":
self.bot.log("Deleting "+self.bot.databaseFuncs.getName(user)+"'s character")
self.bot.database["starwars characters"].delete_one({"_id":user})
return "", "Character for " + self.bot.databaseFuncs.getName(user) + " deleted"
await ctx.send("Character for " + self.bot.databaseFuncs.getName(user) + " deleted")
else:
return "", self.replaceWithSpaces(str(self.charData(user,cmd)))
await ctx.send(self.replaceWithSpaces(str(self.charData(user,cmd))))
if returnEmbed:
em = discord.Embed(title = title, description = text, colour=0xDEADBF)
await ctx.send(embed = em)
def lightsaberChar(self,user : str):
userCharacter = self.bot.database["starwars characters"].find_one({"_id":user})

View File

@ -4,13 +4,13 @@ class StarWarsDestiny():
def destinyNew(self, num : int):
self.bot.log("Creating a new destiny pool with "+str(num)+" players")
roll, diceResults = self.bot.starwarsroll.roll(0,0,0,0,0,0,num)
roll, diceResults = self.bot.starWars.roll.roll(0,0,0,0,0,0,num)
roll = "".join(sorted(roll))
with open("resources/starWars/destinyPoints.txt","wt") as f:
f.write(roll)
return "Rolled for Destiny Points and got:\n"+self.bot.starwarsroll.diceResultToEmoji(diceResults)+"\n"+self.bot.starwarsroll.resultToEmoji(roll)
return "Rolled for Destiny Points and got:\n"+self.bot.starWars.roll.diceResultToEmoji(diceResults)+"\n"+self.bot.starWars.roll.resultToEmoji(roll)
def destinyUse(self, user : str):
with open("resources/starWars/destinyPoints.txt","rt") as f:
@ -24,7 +24,7 @@ class StarWarsDestiny():
with open("resources/starWars/destinyPoints.txt","wt") as f:
f.write(points)
self.bot.log("Did it")
return "Used a dark side destiny point. Destiny pool is now:\n"+self.bot.starwarsroll.resultToEmoji(points)
return "Used a dark side destiny point. Destiny pool is now:\n"+self.bot.starWars.roll.resultToEmoji(points)
else:
self.bot.log("There were no dark side destiny points")
return "No dark side destiny points"
@ -36,31 +36,37 @@ class StarWarsDestiny():
with open("resources/starWars/destinyPoints.txt","wt") as f:
f.write(points)
self.bot.log("Did it")
return "Used a light side destiny point. Destiny pool is now:\n"+self.bot.starwarsroll.resultToEmoji(points)
return "Used a light side destiny point. Destiny pool is now:\n"+self.bot.starWars.roll.resultToEmoji(points)
else:
self.bot.log("There were no dark side destiny points")
return "No light side destiny points"
def parseDestiny(self, user : str, cmd : str):
async def parseDestiny(self, ctx, cmd : str):
user = f"#{ctx.author.id}"
if cmd != "":
while cmd[0] == ' ':
cmd = cmd[1:]
if cmd == "":
break
if cmd == "":
self.bot.log("Retrieving destiny pool info")
with open("resources/starWars/destinyPoints.txt","rt") as f:
return self.bot.starwarsroll.resultToEmoji(f.read())
sendMessage = self.bot.starWars.roll.resultToEmoji(f.read())
else:
commands = cmd.upper().split(" ")
if commands[0] == "N":
if len(commands) > 1:
return self.destinyNew(int(commands[1]))
sendMessage = self.destinyNew(int(commands[1]))
else:
return "You need to give an amount of players (error code 921)"
sendMessage = "You need to give an amount of players (error code 921)"
elif commands[0] == "U":
return self.destinyUse(user)
sendMessage = self.destinyUse(user)
else:
return "I didn't quite understand that (error code 922)"
sendMessage = "I didn't quite understand that (error code 922)"
messageList = sendMessage.split("\n")
await ctx.send(messageList[0])
if len(messageList) > 1:
for messageItem in messageList[1:]:
await ctx.channel.send(messageItem)

View File

@ -239,7 +239,7 @@ class StarWarsRoll():
return random.choice(table)
# Rolls for critical injury
def critRoll(self, addington : int):
async def critRoll(self, ctx, addington : int):
dd = "<:difficulty:690973992470708296>"
sd = "<:setback:690972157890658415>"
bd = "<:boost:690972178216386561>"
@ -253,14 +253,14 @@ class StarWarsRoll():
"**Discouraging Wound**: Flip one light side Destiny point to a dark side Destiny point (reverse if NPC), "+dd] * 5 + [
"**Stunned**: The target is staggered until the end of his next turn, "+dd] * 5 + [
"**Stinger**: Increase the difficulty of next check by one, "+dd] * 5 + [
"**Bowled Over**: The target is knocked prone and suffers 1 sttrain, "+dd+dd] * 5 + [
"**Bowled Over**: The target is knocked prone and suffers 1 strain, "+dd+dd] * 5 + [
"**Head Ringer**: The target increases the difficulty of all Intellect and Cunning checks by one until the end of the encounter, "+dd+dd] * 5 + [
"**Fearsome Wound**: The target increases the difficulty of all Presence and Willpower checks by one until the end of the encounter, "+dd+dd] * 5 + [
"**Agonizing Wound**: The target increases the difficulty of all Brawn and Agility checks by one until the end of the encounter, "+dd+dd] * 5 + [
"**Slightly Dazed**: The target is disoriented until the end of the encounter, "+dd+dd] * 5 + [
"**Scattered Senses**: The target removes all "+bd+" from skill checks until the end of the encounter, "+dd+dd] * 5 + [
"**Hamstrung**: The target loses his free maneuver until the end of the encounter, "+dd+dd] * 5 + [
"**Overpowered**: The target leaves himselp open, and the attacker may immediately attempt another free attack agains him, using the exact same pool as the original, "+dd+dd] * 5 + [
"**Overpowered**: The target leaves himself open, and the attacker may immediately attempt another free attack agains him, using the exact same pool as the original, "+dd+dd] * 5 + [
"**Winded**: Until the end of the encounter, the target cannot voluntarily suffer strain to activate any abilities or gain additional maneuvers, "+dd+dd] * 5 + [
"**Compromised**: Incerase difficulty of all skill checks by one until the end of the encounter, "+dd+dd] * 5 + [
"**At the brink**: The target suffers 1 strain each time he performs an action, "+dd+dd+dd] * 5 + [
@ -288,56 +288,64 @@ class StarWarsRoll():
characteristic = random.choice(["brawn"] * 3 + ["agility"] * 3 + ["intellect", "cunning", "presence"])
results = "**Gruesome Injury**: The target's "+characteristic+" is permanently one lower, "+dd+dd+dd+dd
return "Roll: "+str(roll)+"\nInjury:\n"+results
sendMessage = "Roll: "+str(roll)+"\nInjury:\n"+results
messageList = sendMessage.split("\n")
await ctx.send(messageList[0])
if len(messageList) > 1:
for messageItem in messageList[1:]:
await ctx.channel.send(messageItem)
# Parses the command into something the other functions understand
def parseRoll(self, user : str,cmd : str = ""):
async def parseRoll(self, ctx, cmd : str = ""):
user = f"#{ctx.author.id}"
cmd = re.sub(' +',' ',cmd.upper()) + " "
if cmd[0] == " ":
cmd = cmd[1:]
cmd = self.bot.starwarschar.replaceSpaces(string.capwords(cmd))
cmd = self.bot.starWars.character.replaceSpaces(string.capwords(cmd))
commands = cmd.split(" ")
validCommand = False
if commands[0] == "":
rollParameters = [1,0,3,0,0,0,0]
else:
rollParameters = [0,0,0,0,0,0,0]
if string.capwords(commands[0]) == "Obligations":
try:
return self.obligationRoll()
except:
self.bot.log("Obligation fucked up (error code 911)")
return "An error ocurred (error code 911)"
sendMessage = self.obligationRoll()
elif string.capwords(commands[0]) in skillData:
self.bot.log("Oh look! This guy has skills!")
if self.bot.starwarschar.userHasChar:
if self.bot.starWars.character.userHasChar(user):
self.bot.log("They have a character. That much we know")
skillLevel = self.bot.starwarschar.charData(user,"Skills " + string.capwords(commands[0]))
skillLevel = self.bot.starWars.character.charData(user,"Skills " + string.capwords(commands[0]))
if string.capwords(commands[0]) == "Lightsaber":
self.bot.log("The skill is lightsaber")
charLevel = self.bot.starwarschar.charData(user,"Characteristics " + self.bot.starwarschar.lightsaberChar(user))
charLevel = self.bot.starWars.character.charData(user,"Characteristics " + self.bot.starWars.character.lightsaberChar(user))
else:
charLevel = self.bot.starwarschar.charData(user,"Characteristics " + skillData[string.capwords(commands[0])])
charLevel = self.bot.starWars.character.charData(user,"Characteristics " + skillData[string.capwords(commands[0])])
abilityDice = abs(charLevel-skillLevel)
proficiencyDice = min(skillLevel,charLevel)
commands = [str(abilityDice)] + [str(proficiencyDice)] + commands[1:]
self.bot.log("Converted skill to dice")
validCommand = True
else:
self.bot.log("Okay, no they don't i guess (error code 912)")
return "You don't have a user. You can make one with /starwarscharacter (error code 912)"
self.bot.log("Okay, no they don't i guess")
sendMessage = "You don't have a user. You can make one with /starwarscharacter"
elif string.capwords(commands[0]) in ["Ranged","Piloting"]:
self.bot.log("They fucked up writing the name of a ranged or piloting skill")
if string.capwords(commands[0]) == "Ranged":
return "Did you mean \"Ranged - Heavy\" or \"Ranged - Light\" (error code 913)"
sendMessage = "Did you mean \"Ranged - Heavy\" or \"Ranged - Light\" (error code 913)"
else:
return "Did you mean \"Piloting - Planetary\" or \"Piloting - Space\" (error code 913)"
sendMessage = "Did you mean \"Piloting - Planetary\" or \"Piloting - Space\" (error code 913)"
else:
validCommand = True
try:
if validCommand:
self.bot.log("Converting commands to dice")
for x, command in enumerate(commands):
if command != "":
@ -358,21 +366,26 @@ class StarWarsRoll():
rollParameters[6] = int(command.replace("F",""))
else:
rollParameters[x] = int(command)
except:
self.bot.log("Someone fucked u-up! (it was them) (error code 914)")
return "Invalid input! (error code 914)"
self.bot.log("Rolling "+str(rollParameters))
rollResults, diceResults = self.roll(rollParameters[0],rollParameters[1],rollParameters[2],rollParameters[3],rollParameters[4],rollParameters[5],rollParameters[6])
self.bot.log("Rolling "+str(rollParameters))
rollResults, diceResults = self.roll(rollParameters[0],rollParameters[1],rollParameters[2],rollParameters[3],rollParameters[4],rollParameters[5],rollParameters[6])
simplified = self.simplify(rollResults)
simplified = self.simplify(rollResults)
name = self.bot.starwarschar.getCharName(user)
name = self.bot.starWars.character.getCharName(user)
self.bot.log("Returns results and simplified results")
self.bot.log("Returns results and simplified results")
if simplified == "":
return name + " rolls: " + "\n" + self.diceResultToEmoji(diceResults) + "\nEverything cancels out!"
else:
return name + " rolls: " + "\n" + self.diceResultToEmoji(diceResults) + "\n" + self.resultToEmoji(simplified)
if simplified == "":
sendMessage = name + " rolls: " + "\n" + self.diceResultToEmoji(diceResults) + "\nEverything cancels out!"
else:
sendMessage = name + " rolls: " + "\n" + self.diceResultToEmoji(diceResults) + "\n" + self.resultToEmoji(simplified)
messageList = sendMessage.split("\n")
await ctx.send(messageList[0])
if len(messageList) > 1:
for messageItem in messageList[1:]:
if messageItem == "":
self.bot.log("Tried to send empty message")
else:
await ctx.channel.send(messageItem)

View File

@ -44,6 +44,7 @@ Comments, strings, variable names, class names, docstrings, as well as all other
## Code Style
All the Python code should follow the [PEP 8 guidelines](https://www.python.org/dev/peps/pep-0008/), with the following differences:
+ Variable and function names must be camelCase, and must fully consist of either full words or common/understandable abbreviations.
+ Use f-strings when applicable.
### Documentation
+ Comment lines should not exede 72 characters.
@ -84,7 +85,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.
## 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
### Folders
@ -102,11 +103,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 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.
+ 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.
+ Logs of level `DEBUG` are not printed.
+ Logs of level `ERROR` should only be created in the `on_command_error()` or `Command.error()` functions.
+ 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 `INFO` are not printed.
+ Logs of level `ERROR` should only be created in the `on_command_error()`, `on_error()` or `Command.error()` functions.
### Logging rules
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.

View File

@ -6,7 +6,8 @@ cachetools==4.2.1
certifi==2020.12.5
chardet==3.0.4
d20==1.0.4
discord.py==1.6.0
discord-py-slash-command @ git+https://github.com/eunwoo1104/discord-py-slash-command.git@3c63f9fe9d186c8492e85c3153aff268f1dd5cae
discord.py==1.7.1
dnspython==2.1.0
finnhub-python==2.1.0
gitdb==4.0.7
@ -14,7 +15,9 @@ GitPython==3.1.0
greenlet==1.0.0
idna==2.10
IMDbPY==6.8
inflect==5.3.0
jaraco.context==4.0.0
jaraco.itertools==6.0.1
lark-parser==0.9.0
lxml==4.5.0
more-itertools==8.7.0
@ -24,9 +27,10 @@ numpy==1.18.2
Pillow==7.1.1
pymongo==3.11.3
requests==2.25.1
six==1.15.0
smmap==4.0.0
soupsieve==2.2.1
SQLAlchemy==1.4.3
SQLAlchemy==1.4.5
typing-extensions==3.7.4.3
urllib3==1.25.8
websockets==8.1

View File

@ -1 +1 @@
Kommandoen `/blackjack` starter et spil blackjack. `/blackjack bet [beløb]` lader dig vædde en mængde af dine GwendoBucks. Du bruger `/blackjack hit`, `/blackjack stand`, `/blackjack split` og `/blackjack double` i løbet af spillet.
Kommandoen `/blackjack start` starter et spil blackjack. `/blackjack bet [beløb]` lader dig vædde en mængde af dine GwendoBucks. Du bruger `/blackjack hit`, `/blackjack stand`, `/blackjack split` og `/blackjack double` i løbet af spillet.

View File

@ -40,6 +40,29 @@
}
]
},
"blackjackCards" : {
"base" : "blackjack",
"name" : "cards",
"description" : "Get a count of the cards used in blackjack games"
},
"blackjackDouble" : {
"base" : "blackjack",
"name" : "double",
"description" : "Double your bet in blackjack",
"options" : [
{
"name" : "hand",
"description" : "The number of the hand to double your bet on",
"type" : 4,
"required" : "false"
}
]
},
"blackjackHilo" : {
"base" : "blackjack",
"name" : "hilo",
"description" : "Get the current hi-lo value for the cards used in blackjack games"
},
"blackjackHit" : {
"base" : "blackjack",
"name" : "hit",
@ -53,6 +76,24 @@
}
]
},
"blackjackShuffle" : {
"base" : "blackjack",
"name" : "shuffle",
"description" : "Shuffle the cards used in blackjack games"
},
"blackjackSplit" : {
"base" : "blackjack",
"name" : "split",
"description" : "Split your hand in blackjack",
"options" : [
{
"name" : "hand",
"description" : "The number of the hand to split, in case you've already split once",
"type" : 4,
"required" : "false"
}
]
},
"blackjackStand" : {
"base" : "blackjack",
"name" : "stand",
@ -99,23 +140,10 @@
}
]
},
"connectFourStop" : {
"connectFourSurrender" : {
"base" : "connectFour",
"name" : "stop",
"description" : "Stop the game of connect four"
},
"connectFourPlace" : {
"base" : "connectFour",
"name" : "place",
"description" : "Place a piece",
"options" : [
{
"name" : "column",
"description" : "The column to place the piece",
"type" : 4,
"required" : "true"
}
]
"name" : "surrender",
"description" : "Surrender the game of connect four"
},
"downloading" : {
"name" : "downloading",

View File

@ -1,6 +1,7 @@
"""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 .eventHandlers import EventHandler, ErrorHandler
from .utilFunctions import getParams, logThis, cap, makeFiles, replaceMultiple, emojiToCommand

103
utils/eventHandlers.py Normal file
View File

@ -0,0 +1,103 @@
import discord, traceback, discord_slash, sys
from discord.ext import commands
from .utilFunctions import emojiToCommand
class EventHandler():
def __init__(self, bot):
self.bot = bot
async def on_slash_command(self, ctx):
if ctx.subcommand_name is not None:
subcommand = f" {ctx.subcommand_name} "
else:
subcommand = " "
args = " ".join([str(i) for i in ctx.args])
fullCommand = f"/{ctx.command}{subcommand}{args}"
logMessage = f"{ctx.author.display_name} ran {fullCommand}"
self.bot.log(logMessage, str(ctx.channel_id), level = 25)
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)
async def on_reaction_add(self, reaction, user):
if user.bot == False:
message = reaction.message
channel = message.channel
self.bot.log(f"{user.display_name} reacted to a message",str(channel.id))
try:
connectFourTheirTurn, piece = self.bot.databaseFuncs.connectFourReactionTest(channel,message,"#"+str(user.id))
except:
connectFourTheirTurn = False
bedreNetflixMessage, addMovie, imdbIds = self.bot.databaseFuncs.bedreNetflixReactionTest(channel, message)
if connectFourTheirTurn:
column = emojiToCommand(reaction.emoji)
await self.bot.games.connectFour.placePiece(message, f"#{user.id}", column-1)
elif bedreNetflixMessage and addMovie:
moviePick = emojiToCommand(reaction.emoji)
await message.delete()
if moviePick == "none":
imdbID = None
else:
imdbID = imdbIds[moviePick-1]
await self.bot.other.bedreNetflix.addMovie(channel,imdbID)
elif bedreNetflixMessage and not addMovie:
showPick = emojiToCommand(reaction.emoji)
await message.delete()
if showPick == "none":
imdbName = None
else:
imdbName = imdbIds[showPick-1]
await self.bot.other.bedreNetflix.addShow(channel,imdbName)
elif self.bot.databaseFuncs.hangmanReactionTest(channel, message, f"#{user.id}"):
self.bot.log("They reacted to the hangman message")
if ord(reaction.emoji) in range(127462,127488):
guess = chr(ord(reaction.emoji)-127397)
await self.bot.games.hangman.guess(message, f"#{user.id}", guess)
else:
self.bot.log("Bot they didn't react with a valid guess")
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, discord.errors.NotFound):
self.bot.log("Deleted message before I could add all reactions")
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)
if isinstance(error, discord.errors.NotFound):
self.bot.log("Context is non-existant", level = 40)
else:
await ctx.send("Something went wrong (error code 000)")
async def on_error(self, method):
errorType = sys.exc_info()[0]
if errorType == discord.errors.NotFound:
self.bot.log("Deleted message before I could add all reactions")
else:
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)

View File

@ -53,12 +53,12 @@ class databaseFuncs():
def getName(self, userID):
user = self.bot.database["users"].find_one({"_id":userID})
if user != None:
if userID == f"#{self.bot.user.id}":
return "Gwendolyn"
elif user != None:
return user["user name"]
elif userID == "Gwendolyn":
return userID
else:
self.bot.log("Couldn't find user "+userID)
self.bot.log(f"Couldn't find user {userID}")
return userID
def getID(self,userName):
@ -70,7 +70,7 @@ class databaseFuncs():
self.bot.log("Couldn't find user "+userName)
return None
def deleteGame(self, gameType,channel):
def deleteGame(self, gameType, channel):
self.bot.database[gameType].delete_one({"_id":channel})
def stopServer(self):
@ -78,6 +78,7 @@ class databaseFuncs():
self.bot.database["blackjack games"].delete_many({})
self.bot.database["connect 4 games"].delete_many({})
self.bot.database["hangman games"].delete_many({})
self.bot.database["hex games"].delete_many({})
if not self.bot.options.testing:
g = git.cmd.Git("")
@ -100,7 +101,7 @@ class databaseFuncs():
else:
return False, 0
def hangmanReactionTest(self, channel,message):
def hangmanReactionTest(self, channel, message, user):
try:
with open("resources/games/oldImages/hangman"+str(channel.id), "r") as f:
oldMessages = f.read().splitlines()
@ -111,8 +112,11 @@ class databaseFuncs():
for oldMessage in oldMessages:
oldMessageID = int(oldMessage)
if message.id == oldMessageID:
self.bot.log("They reacted to the hangman game")
gameMessage = True
game = self.bot.database["hangman games"].find_one({"_id":str(channel.id)})
if user == game["player"]:
gameMessage = True
break
return gameMessage

View File

@ -1,11 +1,21 @@
import json
import time
import logging
import os
import sys
from .helperClasses import Options
FORMAT = " %(asctime)s | %(name)-16s | %(levelname)-8s | %(message)s"
PRINTFORMAT = "%(asctime)s - %(message)s"
DATEFORMAT = "%Y-%m-%d %H:%M:%S"
logging.addLevelName(25, "PRINT")
logging.basicConfig(filename="gwendolyn.log", level=logging.INFO)
logging.basicConfig(format=FORMAT, datefmt=DATEFORMAT, level=logging.INFO, filename="gwendolyn.log")
logger = logging.getLogger("Gwendolyn")
printer = logging.getLogger("printer")
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter(fmt = PRINTFORMAT, datefmt=DATEFORMAT))
printer.addHandler(handler)
printer.propagate = False
def getParams():
with open("resources/slashParameters.json", "r") as f:
@ -20,25 +30,24 @@ def getParams():
return params
def logThis(messages, channel : str = "", level : int = 20):
localtime = time.asctime(time.localtime(time.time()))
channel = channel.replace("Direct Message with ","")
if type(messages) is str:
messages = [messages]
printMessage = messages[0]
for x, msg in enumerate(messages):
if channel == "":
messages[x] = localtime+" - "+msg
else:
messages[x] = localtime+" ("+channel+") - "+msg
if channel != "":
messages[x] = f"{msg} - ({channel})"
if len(messages) > 1:
messages[0] += " (details in log)"
printMessage += " (details in log)"
if level >= 25:
print(messages[0])
printer.log(level, printMessage)
for logMessage in messages:
logging.log(level, logMessage)
logger.log(level, logMessage)
# Capitalizes all words except some of them
def cap(s):