Merge pull request #87 from NikolajDanger/PEP_and_docstrings
Pep and docstrings
This commit is contained in:
153 files changed
+6146
-4007
No files matched your search
+12
-12
@@ -153,16 +153,16 @@ static
|
||||
token.txt
|
||||
credentials.txt
|
||||
options.txt
|
||||
resources/starWars/destinyPoints.txt
|
||||
resources/bedreNetflix/
|
||||
resources/games/hilo/
|
||||
resources/games/blackjackTables/
|
||||
resources/games/oldImages/
|
||||
resources/games/connect4Boards/
|
||||
resources/games/hexBoards/
|
||||
resources/games/hangmanBoards/
|
||||
resources/lookup/monsters.json
|
||||
resources/lookup/spells.json
|
||||
resources/movies.txt
|
||||
resources/names.txt
|
||||
gwendolyn/resources/star_wars/destinyPoints.txt
|
||||
gwendolyn/resources/plex/
|
||||
gwendolyn/resources/games/hilo/
|
||||
gwendolyn/resources/games/blackjack_tables/
|
||||
gwendolyn/resources/games/old_images/
|
||||
gwendolyn/resources/games/connect_four_boards/
|
||||
gwendolyn/resources/games/hex_boards/
|
||||
gwendolyn/resources/games/hangman_boards/
|
||||
gwendolyn/resources/lookup/monsters.json
|
||||
gwendolyn/resources/lookup/spells.json
|
||||
gwendolyn/resources/movies.txt
|
||||
gwendolyn/resources/names.txt
|
||||
gwendolynTest.py
|
||||
@@ -1,81 +0,0 @@
|
||||
import os, finnhub, platform, asyncio, discord
|
||||
|
||||
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, EventHandler, ErrorHandler
|
||||
|
||||
class Gwendolyn(commands.Bot):
|
||||
def __init__(self):
|
||||
self.options = Options()
|
||||
self.credentials = Credentials()
|
||||
self.finnhubClient = finnhub.Client(api_key = self.credentials.finnhubKey)
|
||||
self.MongoClient = MongoClient(f"mongodb+srv://{self.credentials.mongoDBUser}:{self.credentials.mongoDBPassword}@gwendolyn.qkwfy.mongodb.net/Gwendolyn?retryWrites=true&w=majority")
|
||||
|
||||
if self.options.testing:
|
||||
self.log("Testing mode")
|
||||
self.database = self.MongoClient["Gwendolyn-Test"]
|
||||
else:
|
||||
self.database = self.MongoClient["Gwendolyn"]
|
||||
|
||||
self.starWars = StarWars(self)
|
||||
self.other = Other(self)
|
||||
self.lookupFuncs = LookupFuncs(self)
|
||||
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
|
||||
|
||||
super().__init__(command_prefix=" ", case_insensitive=True, intents = intents, status = discord.Status.dnd)
|
||||
|
||||
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)")
|
||||
|
||||
async def defer(self, ctx):
|
||||
try:
|
||||
await ctx.defer()
|
||||
except:
|
||||
self.log("defer failed")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if platform.system() == "Windows":
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
||||
# Creates the required files
|
||||
makeFiles()
|
||||
|
||||
# Creates the Bot
|
||||
bot = Gwendolyn()
|
||||
bot.slash = SlashCommand(bot, sync_commands=True, sync_on_cog_reload=True, override_type=True)
|
||||
|
||||
#Loads cogs
|
||||
for filename in os.listdir("./cogs"):
|
||||
if filename.endswith(".py"):
|
||||
bot.load_extension(f"cogs.{filename[:-3]}")
|
||||
|
||||
try:
|
||||
# Runs the whole shabang
|
||||
bot.run(bot.credentials.token)
|
||||
except:
|
||||
bot.log("Could not log in. Remember to write your bot token in the credentials.txt file")
|
||||
@@ -1,34 +0,0 @@
|
||||
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.eventHandler.on_ready()
|
||||
|
||||
# Logs when user sends a command
|
||||
@commands.Cog.listener()
|
||||
async def on_slash_command(self, ctx):
|
||||
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):
|
||||
await self.bot.errorHandler.on_slash_command_error(ctx, error)
|
||||
|
||||
# Logs if on error occurs
|
||||
async def on_error(self, method, *args, **kwargs):
|
||||
await self.bot.errorHandler.on_error(method)
|
||||
|
||||
# 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_reaction_add(self, reaction, user):
|
||||
await self.bot.eventHandler.on_reaction_add(reaction, user)
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(EventCog(bot))
|
||||
@@ -1,155 +0,0 @@
|
||||
from discord.ext import commands
|
||||
from discord_slash import cog_ext
|
||||
|
||||
from utils import getParams
|
||||
|
||||
params = getParams()
|
||||
|
||||
class GamesCog(commands.Cog):
|
||||
def __init__(self,bot):
|
||||
"""Runs game stuff."""
|
||||
self.bot = bot
|
||||
|
||||
# Checks user balance
|
||||
@cog_ext.cog_slash(**params["balance"])
|
||||
async def balance(self, ctx):
|
||||
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 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 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 self.bot.games.trivia.triviaParse(ctx, answer)
|
||||
|
||||
|
||||
class BlackjackCog(commands.Cog):
|
||||
def __init__(self,bot):
|
||||
"""Runs game stuff."""
|
||||
self.bot = bot
|
||||
|
||||
# Starts a game of blackjack
|
||||
@cog_ext.cog_subcommand(**params["blackjackStart"])
|
||||
async def blackjackStart(self, ctx):
|
||||
await self.bot.games.blackjack.start(ctx)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjackBet"])
|
||||
async def blackjackBet(self, ctx, bet):
|
||||
await self.bot.games.blackjack.playerDrawHand(ctx, bet)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjackStand"])
|
||||
async def blackjackStand(self, ctx, hand = ""):
|
||||
await self.bot.games.blackjack.stand(ctx, hand)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjackHit"])
|
||||
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 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 self.bot.games.connectFour.start(ctx, difficulty)
|
||||
|
||||
# Stop the current game of connect four
|
||||
@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 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.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 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 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.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))
|
||||
bot.add_cog(BlackjackCog(bot))
|
||||
bot.add_cog(ConnectFourCog(bot))
|
||||
bot.add_cog(HangmanCog(bot))
|
||||
bot.add_cog(HexCog(bot))
|
||||
@@ -1,24 +0,0 @@
|
||||
from discord.ext import commands
|
||||
from discord_slash import cog_ext
|
||||
|
||||
from utils import getParams
|
||||
|
||||
params = getParams()
|
||||
|
||||
class LookupCog(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
"""Runs lookup commands."""
|
||||
self.bot = bot
|
||||
|
||||
# Looks up a spell
|
||||
@cog_ext.cog_slash(**params["spell"])
|
||||
async def spell(self, ctx, query):
|
||||
await self.bot.lookupFuncs.spellFunc(ctx, query)
|
||||
|
||||
# Looks up a monster
|
||||
@cog_ext.cog_slash(**params["monster"])
|
||||
async def monster(self, ctx, query):
|
||||
await self.bot.lookupFuncs.monsterFunc(ctx, query)
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(LookupCog(bot))
|
||||
@@ -1,94 +0,0 @@
|
||||
import discord, codecs, string, json
|
||||
from discord.ext import commands
|
||||
from discord_slash import cog_ext
|
||||
|
||||
from utils import getParams # pylint: disable=import-error
|
||||
|
||||
params = getParams()
|
||||
|
||||
class MiscCog(commands.Cog):
|
||||
def __init__(self, bot):
|
||||
"""Runs misc commands."""
|
||||
self.bot = bot
|
||||
self.bot.remove_command("help")
|
||||
self.generators = bot.other.generators
|
||||
self.bedreNetflix = bot.other.bedreNetflix
|
||||
self.nerdShit = bot.other.nerdShit
|
||||
|
||||
# Sends the bot's latency
|
||||
@cog_ext.cog_slash(**params["ping"])
|
||||
async def ping(self, ctx):
|
||||
await ctx.send(f"Pong!\nLatency is {round(self.bot.latency * 1000)}ms")
|
||||
|
||||
# Restarts the bot
|
||||
@cog_ext.cog_slash(**params["stop"])
|
||||
async def stop(self, ctx):
|
||||
await self.bot.stop(ctx)
|
||||
|
||||
# Gets help for specific command
|
||||
@cog_ext.cog_slash(**params["help"])
|
||||
async def helpCommand(self, ctx, command = ""):
|
||||
await self.bot.other.helpFunc(ctx, command)
|
||||
|
||||
# Lets you thank the bot
|
||||
@cog_ext.cog_slash(**params["thank"])
|
||||
async def thank(self, ctx):
|
||||
await ctx.send("You're welcome :blush:")
|
||||
|
||||
# Sends a friendly message
|
||||
@cog_ext.cog_slash(**params["hello"])
|
||||
async def hello(self, ctx):
|
||||
await self.bot.other.helloFunc(ctx)
|
||||
|
||||
# Rolls dice
|
||||
@cog_ext.cog_slash(**params["roll"])
|
||||
async def roll(self, ctx, dice = "1d20"):
|
||||
await self.bot.other.rollDice(ctx, dice)
|
||||
|
||||
# Sends a random image
|
||||
@cog_ext.cog_slash(**params["image"])
|
||||
async def image(self, ctx):
|
||||
await self.bot.other.imageFunc(ctx)
|
||||
|
||||
# Finds a random movie
|
||||
@cog_ext.cog_slash(**params["movie"])
|
||||
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 self.generators.nameGen(ctx)
|
||||
|
||||
# Generates a random tavern name
|
||||
@cog_ext.cog_slash(**params["tavern"])
|
||||
async def tavern(self, ctx):
|
||||
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 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 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 self.bedreNetflix.requestShow(ctx, show)
|
||||
|
||||
#Returns currently downloading torrents
|
||||
@cog_ext.cog_slash(**params["downloading"])
|
||||
async def downloading(self, ctx, parameters = "-d"):
|
||||
await self.bedreNetflix.downloading(ctx, parameters)
|
||||
|
||||
#Looks up on Wolfram Alpha
|
||||
@cog_ext.cog_slash(**params["wolf"])
|
||||
async def wolf(self, ctx, query):
|
||||
await self.nerdShit.wolfSearch(ctx, query)
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(MiscCog(bot))
|
||||
@@ -1,37 +0,0 @@
|
||||
import discord, string, json
|
||||
from discord.ext import commands
|
||||
from discord_slash import cog_ext
|
||||
|
||||
from utils import getParams
|
||||
|
||||
params = getParams()
|
||||
|
||||
class starWarsCog(commands.Cog):
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Runs star wars commands."""
|
||||
self.bot = bot
|
||||
|
||||
# Rolls star wars dice
|
||||
@cog_ext.cog_slash(**params["starWarsRoll"])
|
||||
async def starWarsRoll(self, ctx, dice = ""):
|
||||
await self.bot.starWars.roll.parseRoll(ctx, dice)
|
||||
|
||||
# Controls destiny points
|
||||
@cog_ext.cog_slash(**params["starWarsDestiny"])
|
||||
async def starWarsDestiny(self, ctx, parameters = ""):
|
||||
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):
|
||||
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 = ""):
|
||||
await self.bot.starWars.character.parseChar(ctx, parameters)
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(starWarsCog(bot))
|
||||
@@ -1,966 +0,0 @@
|
||||
import random
|
||||
import math
|
||||
import datetime
|
||||
import asyncio
|
||||
import discord
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from shutil import copyfile
|
||||
|
||||
from utils import replaceMultiple
|
||||
|
||||
class Blackjack():
|
||||
def __init__(self,bot):
|
||||
self.bot = bot
|
||||
self.draw = DrawBlackjack(bot)
|
||||
|
||||
# Shuffles the blackjack cards
|
||||
def blackjackShuffle(self, decks, channel):
|
||||
self.bot.log("Shuffling the blackjack deck")
|
||||
|
||||
with open("resources/games/deckOfCards.txt","r") as f:
|
||||
deck = f.read()
|
||||
|
||||
allDecks = deck.split("\n") * decks
|
||||
random.shuffle(allDecks)
|
||||
|
||||
self.bot.database["blackjack cards"].update_one({"_id":channel},{"$set":{"_id":channel,"cards":allDecks}},upsert=True)
|
||||
|
||||
# Creates hilo file
|
||||
self.bot.log("creating hilo doc for "+channel)
|
||||
data = 0
|
||||
self.bot.database["hilo"].update_one({"_id":channel},{"$set":{"_id":channel,"hilo":data}},upsert=True)
|
||||
|
||||
return
|
||||
|
||||
# Calculates the value of a blackjack hand
|
||||
def calcHandValue(self, hand : list):
|
||||
self.bot.log("Calculating hand value")
|
||||
values = []
|
||||
values.append(0)
|
||||
|
||||
for card in hand:
|
||||
cardValue = card[0]
|
||||
cardValue = replaceMultiple(cardValue,["0","k","q","j"],"10")
|
||||
if cardValue == "a":
|
||||
length = len(values)
|
||||
for x in range(length):
|
||||
values.append(values[x] + 11)
|
||||
values[x] += 1
|
||||
else:
|
||||
for x in range(len(values)):
|
||||
values[x] += int(cardValue)
|
||||
|
||||
values.sort()
|
||||
|
||||
handValue = values[0]
|
||||
for value in values:
|
||||
if value <= 21:
|
||||
handValue = value
|
||||
|
||||
self.bot.log("Calculated "+str(hand)+" to be "+str(handValue))
|
||||
|
||||
return handValue
|
||||
|
||||
# Draws a card from the deck
|
||||
def drawCard(self, channel):
|
||||
self.bot.log("drawing a card")
|
||||
|
||||
drawnCard = self.bot.database["blackjack cards"].find_one({"_id":channel})["cards"][0]
|
||||
self.bot.database["blackjack cards"].update_one({"_id":channel},{"$pop":{"cards":-1}})
|
||||
value = self.calcHandValue([drawnCard])
|
||||
|
||||
if value <= 6:
|
||||
self.bot.database["hilo"].update_one({"_id":channel},{"$inc":{"hilo":1}})
|
||||
elif value >= 10:
|
||||
self.bot.database["hilo"].update_one({"_id":channel},{"$inc":{"hilo":-1}})
|
||||
|
||||
return drawnCard
|
||||
|
||||
# Dealer draws a card and checks if they should draw another one
|
||||
def dealerDraw(self,channel):
|
||||
game = self.bot.database["blackjack games"].find_one({"_id":channel})
|
||||
|
||||
done = False
|
||||
dealerHand = game["dealer hand"]
|
||||
|
||||
if self.calcHandValue(dealerHand) < 17:
|
||||
dealerHand.append(self.drawCard(channel))
|
||||
self.bot.database["blackjack games"].update_one({"_id":channel},{"$set":{"dealer hand":dealerHand}})
|
||||
else:
|
||||
done = True
|
||||
|
||||
if self.calcHandValue(dealerHand) > 21:
|
||||
self.bot.database["blackjack games"].update_one({"_id":channel},{"$set":{"dealer busted":True}})
|
||||
|
||||
return done
|
||||
|
||||
# Goes to the next round and calculates some stuff
|
||||
def blackjackContinue(self, channel):
|
||||
self.bot.log("Continuing blackjack game")
|
||||
game = self.bot.database["blackjack games"].find_one({"_id":channel})
|
||||
|
||||
done = False
|
||||
|
||||
self.bot.database["blackjack games"].update_one({"_id":channel},{"$inc":{"round":1}})
|
||||
|
||||
allStanding = True
|
||||
preAllStanding = True
|
||||
message = "All players are standing. The dealer now shows his cards and draws."
|
||||
|
||||
if game["all standing"]:
|
||||
self.bot.log("All are standing")
|
||||
|
||||
done = self.dealerDraw(channel)
|
||||
message = "The dealer draws a card."
|
||||
|
||||
game = self.bot.database["blackjack games"].find_one({"_id":channel})
|
||||
|
||||
self.bot.log("Testing if all are standing")
|
||||
for user in game["user hands"]:
|
||||
try:
|
||||
newUser, allStanding, preAllStanding = self.testIfStanding(game["user hands"][user],allStanding,preAllStanding,True)
|
||||
self.bot.database["blackjack games"].update_one({"_id":channel},{"$set":{"user hands."+user:newUser}})
|
||||
except:
|
||||
self.bot.log("Error in testing if all are standing (error code 1331)")
|
||||
|
||||
if allStanding:
|
||||
self.bot.database["blackjack games"].update_one({"_id":channel},{"$set":{"all standing":True}})
|
||||
|
||||
try:
|
||||
self.draw.drawImage(channel)
|
||||
except:
|
||||
self.bot.log("Error drawing blackjack table (error code 1340)")
|
||||
|
||||
if allStanding:
|
||||
if done == False:
|
||||
return message, True, done
|
||||
else:
|
||||
return "The dealer is done drawing cards", True, done
|
||||
elif preAllStanding:
|
||||
return "", True, done
|
||||
else:
|
||||
if game["round"] == 1:
|
||||
firstRoundMessage = ". You can also double down with \"/blackjack double\" or split with \"/blackjack split\""
|
||||
else:
|
||||
firstRoundMessage = ""
|
||||
return "You have 2 minutes to either hit or stand with \"/blackjack hit\" or \"/blackjack stand\""+firstRoundMessage+". It's assumed you're standing if you don't make a choice.", False, done
|
||||
|
||||
def testIfStanding(self, hand,allStanding,preAllStanding,topLevel):
|
||||
if hand["hit"] == False:
|
||||
hand["standing"] = True
|
||||
|
||||
if hand["standing"] == False:
|
||||
allStanding = False
|
||||
|
||||
if self.calcHandValue(hand["hand"]) >= 21 or hand["doubled"]:
|
||||
hand["standing"] = True
|
||||
else:
|
||||
preAllStanding = False
|
||||
|
||||
hand["hit"] = False
|
||||
|
||||
if topLevel:
|
||||
if hand["split"] >= 1:
|
||||
hand["other hand"], allStanding, preAllStanding = self.testIfStanding(hand["other hand"],allStanding,preAllStanding,False)
|
||||
if hand["split"] >= 2:
|
||||
hand["third hand"], allStanding, preAllStanding = self.testIfStanding(hand["third hand"],allStanding,preAllStanding,False)
|
||||
if hand["split"] >= 3:
|
||||
hand["fourth hand"], allStanding, preAllStanding = self.testIfStanding(hand["fourth hand"],allStanding,preAllStanding,False)
|
||||
|
||||
return hand, allStanding, preAllStanding
|
||||
|
||||
# When players try to hit
|
||||
async def hit(self, ctx, handNumber = 0):
|
||||
await self.bot.defer(ctx)
|
||||
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"]:
|
||||
|
||||
userHands = game["user hands"][user]
|
||||
hand, handNumber = self.getHandNumber(userHands, handNumber)
|
||||
|
||||
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:
|
||||
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
|
||||
async def double(self, ctx, handNumber = 0):
|
||||
await self.bot.defer(ctx)
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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
|
||||
async def stand(self, ctx, handNumber = 0):
|
||||
await self.bot.defer(ctx)
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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
|
||||
async def split(self, ctx, handNumber = 0):
|
||||
await self.bot.defer(ctx)
|
||||
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:
|
||||
hand = game["user hands"][user]
|
||||
newHand = game["user hands"][user]["other hand"]
|
||||
handNumber = 0
|
||||
otherHand = 2
|
||||
else:
|
||||
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:
|
||||
handNumberError = True
|
||||
|
||||
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 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:
|
||||
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
|
||||
async def playerDrawHand(self, ctx, bet : int):
|
||||
await self.bot.defer(ctx)
|
||||
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(f"{userName} is trying to join the Blackjack 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.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
|
||||
async def start(self, ctx):
|
||||
await self.bot.defer(ctx)
|
||||
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:
|
||||
|
||||
dealerHand = [self.drawCard(channel),self.drawCard(channel)]
|
||||
gameID = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
||||
|
||||
newGame = {"_id":channel,"dealer hand": dealerHand,"dealer busted":False,"dealer blackjack":False,"user hands": {},"all standing":False,"round":0,"gameID":gameID}
|
||||
|
||||
if self.calcHandValue(dealerHand) == 21:
|
||||
newGame["dealer blackjack"] = True
|
||||
|
||||
self.bot.database["blackjack games"].insert_one(newGame)
|
||||
|
||||
copyfile("resources/games/blackjackTable.png","resources/games/blackjackTables/blackjackTable"+channel+".png")
|
||||
|
||||
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:
|
||||
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):
|
||||
finalWinnings = "*Final Winnings:*\n"
|
||||
|
||||
game = self.bot.database["blackjack games"].find_one({"_id":channel})
|
||||
|
||||
dealerValue = self.calcHandValue(game["dealer hand"])
|
||||
dealerBlackjack = game["dealer blackjack"]
|
||||
dealerBusted = game["dealer busted"]
|
||||
|
||||
try:
|
||||
for user in game["user hands"]:
|
||||
|
||||
winnings, netWinnings, reason = self.calcWinnings(game["user hands"][user],dealerValue,True,dealerBlackjack,dealerBusted)
|
||||
|
||||
if winnings < 0:
|
||||
if winnings == -1:
|
||||
finalWinnings += self.bot.databaseFuncs.getName(user)+" lost "+str(-1 * winnings)+" GwendoBuck "+reason+"\n"
|
||||
else:
|
||||
finalWinnings += self.bot.databaseFuncs.getName(user)+" lost "+str(-1 * winnings)+" GwendoBucks "+reason+"\n"
|
||||
else:
|
||||
if winnings == 1:
|
||||
finalWinnings += self.bot.databaseFuncs.getName(user)+" won "+str(winnings)+" GwendoBuck "+reason+"\n"
|
||||
else:
|
||||
finalWinnings += self.bot.databaseFuncs.getName(user)+" won "+str(winnings)+" GwendoBucks "+reason+"\n"
|
||||
|
||||
self.bot.money.addMoney(user,netWinnings)
|
||||
|
||||
except:
|
||||
self.bot.log("Error calculating winnings (error code 1311)")
|
||||
|
||||
self.bot.database["blackjack games"].delete_one({"_id":channel})
|
||||
|
||||
return finalWinnings
|
||||
|
||||
def calcWinnings(self,hand, dealerValue, topLevel, dealerBlackjack, dealerBusted):
|
||||
self.bot.log("Calculating winnings")
|
||||
reason = ""
|
||||
bet = hand["bet"]
|
||||
winnings = -1 * bet
|
||||
netWinnings = 0
|
||||
handValue = self.calcHandValue(hand["hand"])
|
||||
|
||||
if hand["blackjack"] and dealerBlackjack == False:
|
||||
reason += "(blackjack)"
|
||||
winnings += math.floor(2.5 * bet)
|
||||
netWinnings += math.floor(2.5 * bet)
|
||||
elif dealerBlackjack:
|
||||
reason += "(dealer blackjack)"
|
||||
elif hand["busted"]:
|
||||
reason += "(busted)"
|
||||
else:
|
||||
if dealerBusted:
|
||||
reason = "(dealer busted)"
|
||||
winnings += 2 * bet
|
||||
netWinnings += 2 * bet
|
||||
elif handValue > dealerValue:
|
||||
winnings += 2 * bet
|
||||
netWinnings += 2 * bet
|
||||
reason = "(highest value)"
|
||||
elif handValue == dealerValue:
|
||||
reason = "(pushed)"
|
||||
winnings += bet
|
||||
netWinnings += bet
|
||||
else:
|
||||
reason = "(highest value)"
|
||||
|
||||
if topLevel:
|
||||
if hand["split"] >= 1:
|
||||
winningsTemp, netWinningsTemp, reasonTemp = self.calcWinnings(hand["other hand"],dealerValue,False,dealerBlackjack,dealerBusted)
|
||||
winnings += winningsTemp
|
||||
netWinnings += netWinningsTemp
|
||||
reason += reasonTemp
|
||||
if hand["split"] >= 2:
|
||||
winningsTemp, netWinningsTemp, reasonTemp = self.calcWinnings(hand["third hand"],dealerValue,False,dealerBlackjack,dealerBusted)
|
||||
winnings += winningsTemp
|
||||
netWinnings += netWinningsTemp
|
||||
reason += reasonTemp
|
||||
if hand["split"] >= 3:
|
||||
winningsTemp, netWinningsTemp, reasonTemp = self.calcWinnings(hand["fourth hand"],dealerValue,False,dealerBlackjack,dealerBusted)
|
||||
winnings += winningsTemp
|
||||
netWinnings += netWinningsTemp
|
||||
reason += reasonTemp
|
||||
|
||||
return winnings, netWinnings, reason
|
||||
|
||||
def getHandNumber(self, user,handNumber):
|
||||
try:
|
||||
hand = None
|
||||
|
||||
if user["split"] == 0:
|
||||
hand = user
|
||||
handNumber = 0
|
||||
else:
|
||||
if handNumber != 0:
|
||||
if handNumber == 1:
|
||||
hand = user
|
||||
elif handNumber == 2:
|
||||
hand = user["other hand"]
|
||||
elif handNumber == 3:
|
||||
hand = user["third hand"]
|
||||
elif handNumber == 4:
|
||||
hand = user["fourth hand"]
|
||||
|
||||
return hand, handNumber
|
||||
except:
|
||||
self.bot.log("Problem with getHandNumber() (error code 1322)")
|
||||
|
||||
def isRoundDone(self,game):
|
||||
roundDone = True
|
||||
|
||||
for person in game["user hands"].values():
|
||||
if person["hit"] == False and person["standing"] == False:
|
||||
roundDone = False
|
||||
|
||||
if person["split"] > 0:
|
||||
if person["other hand"]["hit"] == False and person["other hand"]["standing"] == False:
|
||||
roundDone = False
|
||||
|
||||
if person["split"] > 1:
|
||||
if person["third hand"]["hit"] == False and person["third hand"]["standing"] == False:
|
||||
roundDone = False
|
||||
|
||||
if person["split"] > 2:
|
||||
if person["fourth hand"]["hit"] == False and person["fourth hand"]["standing"] == False:
|
||||
roundDone = False
|
||||
|
||||
return roundDone
|
||||
|
||||
# Loop of blackjack game rounds
|
||||
async def blackjackLoop(self,channel,gameRound,gameID):
|
||||
self.bot.log("Loop "+str(gameRound),str(channel.id))
|
||||
|
||||
with open("resources/games/oldImages/blackjack"+str(channel.id), "r") as f:
|
||||
oldImage = await channel.fetch_message(int(f.read()))
|
||||
|
||||
new_message, allStanding, gamedone = self.blackjackContinue(str(channel.id))
|
||||
if new_message != "":
|
||||
self.bot.log(new_message,str(channel.id))
|
||||
await channel.send(new_message)
|
||||
if gamedone == False:
|
||||
await oldImage.delete()
|
||||
oldImage = await channel.send(file = discord.File("resources/games/blackjackTables/blackjackTable"+str(channel.id)+".png"))
|
||||
with open("resources/games/oldImages/blackjack"+str(channel.id), "w") as f:
|
||||
f.write(str(oldImage.id))
|
||||
|
||||
try:
|
||||
if allStanding:
|
||||
await asyncio.sleep(5)
|
||||
else:
|
||||
await asyncio.sleep(120)
|
||||
except:
|
||||
self.bot.log("Loop "+str(gameRound)+" interrupted (error code 1321)")
|
||||
|
||||
game = self.bot.database["blackjack games"].find_one({"_id":str(channel.id)})
|
||||
|
||||
if game != None:
|
||||
realRound = game["round"]
|
||||
realGameID = game["gameID"]
|
||||
|
||||
if gameRound == realRound and realGameID == gameID:
|
||||
if gamedone == False:
|
||||
self.bot.log("Loop "+str(gameRound)+" calling self.blackjackLoop()",str(channel.id))
|
||||
await self.blackjackLoop(channel,gameRound+1,gameID)
|
||||
else:
|
||||
try:
|
||||
new_message = self.blackjackFinish(str(channel.id))
|
||||
except:
|
||||
self.bot.log("Something fucked up (error code 1310)")
|
||||
await channel.send(new_message)
|
||||
else:
|
||||
self.bot.log("Ending loop on round "+str(gameRound),str(channel.id))
|
||||
else:
|
||||
self.bot.log("Ending loop on round "+str(gameRound),str(channel.id))
|
||||
|
||||
# Returning current hi-lo value
|
||||
async def hilo(self, ctx):
|
||||
channel = ctx.channel_id
|
||||
data = self.bot.database["hilo"].find_one({"_id":str(channel)})
|
||||
if data != None:
|
||||
hilo = str(data["hilo"])
|
||||
else:
|
||||
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)
|
||||
|
||||
class DrawBlackjack():
|
||||
def __init__(self,bot):
|
||||
self.bot = bot
|
||||
self.BORDER = 100
|
||||
self.PLACEMENT = [0,0]
|
||||
self.ROTATION = 0
|
||||
|
||||
def drawImage(self,channel):
|
||||
self.bot.log("Drawing blackjack table",channel)
|
||||
game = self.bot.database["blackjack games"].find_one({"_id":channel})
|
||||
|
||||
fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', 50)
|
||||
fntSmol = ImageFont.truetype('resources/fonts/futura-bold.ttf', 40)
|
||||
self.BORDERSmol = int(self.BORDER/3.5)
|
||||
|
||||
table = Image.open("resources/games/blackjackTable.png")
|
||||
self.PLACEMENT = [2,1,3,0,4]
|
||||
textImage = ImageDraw.Draw(table)
|
||||
hands = game["user hands"]
|
||||
|
||||
dealerBusted = game["dealer busted"]
|
||||
dealerBlackjack = game["dealer blackjack"]
|
||||
|
||||
try:
|
||||
if game["all standing"] == False:
|
||||
dealerHand = self.drawHand(game["dealer hand"],True,False,False)
|
||||
else:
|
||||
dealerHand = self.drawHand(game["dealer hand"],False,dealerBusted,dealerBlackjack)
|
||||
except:
|
||||
self.bot.log("Error drawing dealer hand (error code 1341a)")
|
||||
|
||||
table.paste(dealerHand,(800-self.BORDERSmol,20-self.BORDERSmol),dealerHand)
|
||||
|
||||
for x in range(len(hands)):
|
||||
key, value = list(hands.items())[x]
|
||||
key = self.bot.databaseFuncs.getName(key)
|
||||
#self.bot.log("Drawing "+key+"'s hand")
|
||||
userHand = self.drawHand(value["hand"],False,value["busted"],value["blackjack"])
|
||||
try:
|
||||
if value["split"] == 3:
|
||||
table.paste(userHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),280-self.BORDERSmol),userHand)
|
||||
userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"])
|
||||
table.paste(userOtherHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),420-self.BORDERSmol),userOtherHand)
|
||||
userThirdHand = self.drawHand(value["third hand"]["hand"],False,value["third hand"]["busted"],value["third hand"]["blackjack"])
|
||||
table.paste(userThirdHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),560-self.BORDERSmol),userThirdHand)
|
||||
userFourthHand = self.drawHand(value["fourth hand"]["hand"],False,value["fourth hand"]["busted"],value["fourth hand"]["blackjack"])
|
||||
table.paste(userFourthHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),700-self.BORDERSmol),userFourthHand)
|
||||
elif value["split"] == 2:
|
||||
table.paste(userHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),420-self.BORDERSmol),userHand)
|
||||
userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"])
|
||||
table.paste(userOtherHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),560-self.BORDERSmol),userOtherHand)
|
||||
userThirdHand = self.drawHand(value["third hand"]["hand"],False,value["third hand"]["busted"],value["third hand"]["blackjack"])
|
||||
table.paste(userThirdHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),700-self.BORDERSmol),userThirdHand)
|
||||
elif value["split"] == 1:
|
||||
table.paste(userHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),560-self.BORDERSmol),userHand)
|
||||
userOtherHand = self.drawHand(value["other hand"]["hand"],False,value["other hand"]["busted"],value["other hand"]["blackjack"])
|
||||
table.paste(userOtherHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),700-self.BORDERSmol),userOtherHand)
|
||||
else:
|
||||
table.paste(userHand,(32-self.BORDERSmol+(384*self.PLACEMENT[x]),680-self.BORDERSmol),userHand)
|
||||
except:
|
||||
self.bot.log("Error drawing player hands (error code 1341b)")
|
||||
|
||||
textWidth = fnt.getsize(key)[0]
|
||||
if textWidth < 360:
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)-3,1010-3),key,fill=(0,0,0), font=fnt)
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)+3,1010-3),key,fill=(0,0,0), font=fnt)
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)-3,1010+3),key,fill=(0,0,0), font=fnt)
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)+3,1010+3),key,fill=(0,0,0), font=fnt)
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2),1005),key,fill=(255,255,255), font=fnt)
|
||||
else:
|
||||
textWidth = fntSmol.getsize(key)[0]
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)-2,1020-2),key,fill=(0,0,0), font=fntSmol)
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)+2,1020-2),key,fill=(0,0,0), font=fntSmol)
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)-2,1020+2),key,fill=(0,0,0), font=fntSmol)
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2)+2,1020+2),key,fill=(0,0,0), font=fntSmol)
|
||||
textImage.text((32+(384*self.PLACEMENT[x])+117-int(textWidth/2),1015),key,fill=(255,255,255), font=fntSmol)
|
||||
|
||||
self.bot.log("Saving table image")
|
||||
table.save("resources/games/blackjackTables/blackjackTable"+channel+".png")
|
||||
|
||||
return
|
||||
|
||||
def drawHand(self, hand, dealer, busted, blackjack):
|
||||
self.bot.log("Drawing hand "+str(hand)+", "+str(busted)+", "+str(blackjack))
|
||||
fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', 200)
|
||||
fnt2 = ImageFont.truetype('resources/fonts/futura-bold.ttf', 120)
|
||||
length = len(hand)
|
||||
background = Image.new("RGBA", ((self.BORDER*2)+691+(125*(length-1)),(self.BORDER*2)+1065),(0,0,0,0))
|
||||
textImage = ImageDraw.Draw(background)
|
||||
|
||||
if dealer:
|
||||
img = Image.open("resources/games/cards/"+hand[0].upper()+".png")
|
||||
#self.ROTATION = (random.randint(-20,20)/10.0)
|
||||
img = img.rotate(self.ROTATION,expand = 1)
|
||||
#self.PLACEMENT = [random.randint(-20,20),random.randint(-20,20)]
|
||||
background.paste(img,(self.BORDER+self.PLACEMENT[0],self.BORDER+self.PLACEMENT[1]),img)
|
||||
img = Image.open("resources/games/cards/red_back.png")
|
||||
#self.ROTATION = (random.randint(-20,20)/10.0)
|
||||
img = img.rotate(self.ROTATION,expand = 1)
|
||||
#self.PLACEMENT = [random.randint(-20,20),random.randint(-20,20)]
|
||||
background.paste(img,(125+self.BORDER+self.PLACEMENT[0],self.BORDER+self.PLACEMENT[1]),img)
|
||||
else:
|
||||
for x in range(length):
|
||||
img = Image.open("resources/games/cards/"+hand[x].upper()+".png")
|
||||
#self.ROTATION = (random.randint(-20,20)/10.0)
|
||||
img = img.rotate(self.ROTATION,expand = 1)
|
||||
#self.PLACEMENT = [random.randint(-20,20),random.randint(-20,20)]
|
||||
background.paste(img,(self.BORDER+(x*125)+self.PLACEMENT[0],self.BORDER+self.PLACEMENT[1]),img)
|
||||
|
||||
w, h = background.size
|
||||
textHeight = 290+self.BORDER
|
||||
|
||||
#self.bot.log("Drawing busted/blackjack")
|
||||
if busted:
|
||||
textWidth = fnt.getsize("BUSTED")[0]
|
||||
textImage.text((int(w/2)-int(textWidth/2)-10,textHeight+20-10),"BUSTED",fill=(0,0,0), font=fnt)
|
||||
textImage.text((int(w/2)-int(textWidth/2)+10,textHeight+20-10),"BUSTED",fill=(0,0,0), font=fnt)
|
||||
textImage.text((int(w/2)-int(textWidth/2)-10,textHeight+20+10),"BUSTED",fill=(0,0,0), font=fnt)
|
||||
textImage.text((int(w/2)-int(textWidth/2)+10,textHeight+20+10),"BUSTED",fill=(0,0,0), font=fnt)
|
||||
textImage.text((int(w/2)-int(textWidth/2)-5,textHeight-5),"BUSTED",fill=(255,255,255), font=fnt)
|
||||
textImage.text((int(w/2)-int(textWidth/2)+5,textHeight-5),"BUSTED",fill=(255,255,255), font=fnt)
|
||||
textImage.text((int(w/2)-int(textWidth/2)-5,textHeight+5),"BUSTED",fill=(255,255,225), font=fnt)
|
||||
textImage.text((int(w/2)-int(textWidth/2)+5,textHeight+5),"BUSTED",fill=(255,255,255), font=fnt)
|
||||
textImage.text((int(w/2)-int(textWidth/2),textHeight),"BUSTED",fill=(255,50,50), font=fnt)
|
||||
elif blackjack:
|
||||
textWidth = fnt2.getsize("BLACKJACK")[0]
|
||||
textImage.text((int(w/2)-int(textWidth/2)-6,textHeight+20-6),"BLACKJACK",fill=(0,0,0), font=fnt2)
|
||||
textImage.text((int(w/2)-int(textWidth/2)+6,textHeight+20-6),"BLACKJACK",fill=(0,0,0), font=fnt2)
|
||||
textImage.text((int(w/2)-int(textWidth/2)-6,textHeight+20+6),"BLACKJACK",fill=(0,0,0), font=fnt2)
|
||||
textImage.text((int(w/2)-int(textWidth/2)+6,textHeight+20+6),"BLACKJACK",fill=(0,0,0), font=fnt2)
|
||||
textImage.text((int(w/2)-int(textWidth/2)-3,textHeight-3),"BLACKJACK",fill=(255,255,255), font=fnt2)
|
||||
textImage.text((int(w/2)-int(textWidth/2)+3,textHeight-3),"BLACKJACK",fill=(255,255,255), font=fnt2)
|
||||
textImage.text((int(w/2)-int(textWidth/2)-3,textHeight+3),"BLACKJACK",fill=(255,255,255), font=fnt2)
|
||||
textImage.text((int(w/2)-int(textWidth/2)+3,textHeight+3),"BLACKJACK",fill=(255,255,255), font=fnt2)
|
||||
textImage.text((int(w/2)-int(textWidth/2),textHeight),"BLACKJACK",fill=(155,123,0), font=fnt2)
|
||||
|
||||
#self.bot.log("Returning resized image")
|
||||
return background.resize((int(w/3.5),int(h/3.5)),resample=Image.BILINEAR)
|
||||
|
||||
@@ -1,583 +0,0 @@
|
||||
import random
|
||||
import copy
|
||||
import math
|
||||
import discord
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
class ConnectFour():
|
||||
def __init__(self,bot):
|
||||
self.bot = bot
|
||||
self.draw = drawConnectFour(bot)
|
||||
self.AISCORES = {
|
||||
"middle": 3,
|
||||
"two in a row": 10,
|
||||
"three in a row": 50,
|
||||
"enemy two in a row": -35,
|
||||
"enemy three in a row": -200,
|
||||
"enemy win": -10000,
|
||||
"win": 10000,
|
||||
"avoid losing": 100
|
||||
}
|
||||
|
||||
self.ROWCOUNT = 6
|
||||
self.COLUMNCOUNT = 7
|
||||
|
||||
# Starts the game
|
||||
async def start(self, ctx, opponent):
|
||||
await self.bot.defer(ctx)
|
||||
user = f"#{ctx.author.id}"
|
||||
channel = str(ctx.channel_id)
|
||||
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
|
||||
|
||||
startedGame = False
|
||||
canStart = True
|
||||
|
||||
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
|
||||
if ctx.author != opponent:
|
||||
opponent = f"#{opponent.id}"
|
||||
difficulty = 5
|
||||
diffText = ""
|
||||
else:
|
||||
sendMessage = "You can't play against yourself"
|
||||
logMessage = "They tried to play against themself"
|
||||
canStart = False
|
||||
|
||||
if canStart:
|
||||
board = [[0 for _ in range(self.COLUMNCOUNT)] for _ in range(self.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}
|
||||
|
||||
self.bot.database["connect 4 games"].insert_one(newGame)
|
||||
|
||||
self.draw.drawImage(channel)
|
||||
|
||||
gwendoTurn = (players[0] == f"#{self.bot.user.id}")
|
||||
startedGame = True
|
||||
|
||||
opponentName = self.bot.databaseFuncs.getName(opponent)
|
||||
turnName = self.bot.databaseFuncs.getName(players[0])
|
||||
|
||||
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
|
||||
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 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)
|
||||
|
||||
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}})
|
||||
|
||||
self.bot.log("Checking for win")
|
||||
won, winDirection, winCoordinates = self.isWon(board)
|
||||
|
||||
if won != 0:
|
||||
gameWon = True
|
||||
self.bot.database["connect 4 games"].update_one({"_id":channel},{"$set":{"winner":won}})
|
||||
self.bot.database["connect 4 games"].update_one({"_id":channel},{"$set":{"win direction":winDirection}})
|
||||
self.bot.database["connect 4 games"].update_one({"_id":channel},
|
||||
{"$set":{"win coordinates":winCoordinates}})
|
||||
|
||||
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] != f"#{self.bot.user.id}":
|
||||
sendMessage += " Adding "+str(winAmount)+" GwendoBucks to their account"
|
||||
elif 0 not in board[0]:
|
||||
gameWon = True
|
||||
sendMessage = "It's a draw!"
|
||||
logMessage = "The game ended in a draw"
|
||||
else:
|
||||
gameWon = False
|
||||
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 = (game["players"][turn] == f"#{self.bot.user.id}")
|
||||
|
||||
placedPiece = True
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
for x, line in enumerate(board):
|
||||
if line[column] == 0:
|
||||
placementX = x
|
||||
|
||||
board[placementX][placementY] = player
|
||||
|
||||
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
|
||||
async def surrender(self, ctx):
|
||||
await self.bot.defer(ctx)
|
||||
channel = str(ctx.channel_id)
|
||||
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)
|
||||
|
||||
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:
|
||||
self.bot.log("The old image was already deleted")
|
||||
|
||||
self.endGame(channel)
|
||||
else:
|
||||
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):
|
||||
won = 0
|
||||
winDirection = ""
|
||||
winCoordinates = [0,0]
|
||||
|
||||
for row in range(self.ROWCOUNT):
|
||||
for place in range(self.COLUMNCOUNT):
|
||||
if won == 0:
|
||||
piecePlayer = board[row][place]
|
||||
if piecePlayer != 0:
|
||||
# Checks horizontal
|
||||
if place <= self.COLUMNCOUNT-4:
|
||||
pieces = [board[row][place+1],board[row][place+2],board[row][place+3]]
|
||||
else:
|
||||
pieces = [0]
|
||||
|
||||
if all(x == piecePlayer for x in pieces):
|
||||
won = piecePlayer
|
||||
winDirection = "h"
|
||||
winCoordinates = [row,place]
|
||||
|
||||
# Checks vertical
|
||||
if row <= self.ROWCOUNT-4:
|
||||
pieces = [board[row+1][place],board[row+2][place],board[row+3][place]]
|
||||
else:
|
||||
pieces = [0]
|
||||
|
||||
if all(x == piecePlayer for x in pieces):
|
||||
won = piecePlayer
|
||||
winDirection = "v"
|
||||
winCoordinates = [row,place]
|
||||
|
||||
# Checks right diagonal
|
||||
if row <= self.ROWCOUNT-4 and place <= self.COLUMNCOUNT-4:
|
||||
pieces = [board[row+1][place+1],board[row+2][place+2],board[row+3][place+3]]
|
||||
else:
|
||||
pieces = [0]
|
||||
|
||||
if all(x == piecePlayer for x in pieces):
|
||||
won = piecePlayer
|
||||
winDirection = "r"
|
||||
winCoordinates = [row,place]
|
||||
|
||||
# Checks left diagonal
|
||||
if row <= self.ROWCOUNT-4 and place >= 3:
|
||||
pieces = [board[row+1][place-1],board[row+2][place-2],board[row+3][place-3]]
|
||||
else:
|
||||
pieces = [0]
|
||||
|
||||
if all(x == piecePlayer for x in pieces):
|
||||
won = piecePlayer
|
||||
winDirection = "l"
|
||||
winCoordinates = [row,place]
|
||||
|
||||
|
||||
return won, winDirection, winCoordinates
|
||||
|
||||
# Plays as the AI
|
||||
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(f"#{self.bot.user.id}")+1
|
||||
difficulty = game["difficulty"]
|
||||
|
||||
scores = [-math.inf for _ in range(self.COLUMNCOUNT)]
|
||||
for column in range(self.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(f"Best score for column {column} is {scores[column]}")
|
||||
|
||||
possibleScores = scores.copy()
|
||||
|
||||
while (min(possibleScores) <= (max(possibleScores) - max(possibleScores)/10)) and len(possibleScores) != 1:
|
||||
possibleScores.remove(min(possibleScores))
|
||||
|
||||
highest_score = random.choice(possibleScores)
|
||||
|
||||
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):
|
||||
score = 0
|
||||
otherPlayer = player%2+1
|
||||
|
||||
# Adds points for middle placement
|
||||
# Checks horizontal
|
||||
for row in range(self.ROWCOUNT):
|
||||
if board[row][3] == player:
|
||||
score += self.AISCORES["middle"]
|
||||
rowArray = [int(i) for i in list(board[row])]
|
||||
for place in range(self.COLUMNCOUNT-3):
|
||||
window = rowArray[place:place+4]
|
||||
score += self.evaluateWindow(window,player,otherPlayer)
|
||||
|
||||
# Checks Vertical
|
||||
for column in range(self.COLUMNCOUNT):
|
||||
columnArray = [int(i[column]) for i in list(board)]
|
||||
for place in range(self.ROWCOUNT-3):
|
||||
window = columnArray[place:place+4]
|
||||
score += self.evaluateWindow(window,player,otherPlayer)
|
||||
|
||||
# Checks right diagonal
|
||||
for row in range(self.ROWCOUNT-3):
|
||||
for place in range(self.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,self.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)
|
||||
|
||||
|
||||
## Checks if anyone has won
|
||||
#won = isWon(board)[0]
|
||||
|
||||
## Add points if AI wins
|
||||
#if won == player:
|
||||
# score += self.AISCORES["win"]
|
||||
|
||||
return score
|
||||
|
||||
def evaluateWindow(self, window,player,otherPlayer):
|
||||
if window.count(player) == 4:
|
||||
return self.AISCORES["win"]
|
||||
elif window.count(player) == 3 and window.count(0) == 1:
|
||||
return self.AISCORES["three in a row"]
|
||||
elif window.count(player) == 2 and window.count(0) == 2:
|
||||
return self.AISCORES["two in a row"]
|
||||
elif window.count(otherPlayer) == 4:
|
||||
return self.AISCORES["enemy win"]
|
||||
else:
|
||||
return 0
|
||||
|
||||
async def minimax(self, board, depth, player , originalPlayer, alpha, beta, maximizingPlayer):
|
||||
#terminal = ((0 not in board[0]) or (isWon(board)[0] != 0))
|
||||
terminal = 0 not in board[0]
|
||||
points = self.AICalcPoints(board,originalPlayer)
|
||||
# The depth is how many moves ahead the computer checks. This value is the difficulty.
|
||||
if depth == 0 or terminal or (points > 5000 or points < -6000):
|
||||
return points
|
||||
if maximizingPlayer:
|
||||
value = -math.inf
|
||||
for column in range(0,self.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 += self.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,self.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 += self.AISCORES["avoid losing"]
|
||||
value = min(value,evaluation)
|
||||
beta = min(beta,evaluation)
|
||||
if beta <= alpha: break
|
||||
return value
|
||||
|
||||
class drawConnectFour():
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
# Draws the whole thing
|
||||
def drawImage(self, channel):
|
||||
self.bot.log("Drawing connect four board")
|
||||
game = self.bot.database["connect 4 games"].find_one({"_id":channel})
|
||||
|
||||
board = game["board"]
|
||||
|
||||
border = 40
|
||||
gridBorder = 40
|
||||
cornerSize = 300
|
||||
boardOutlineSize = 10
|
||||
pieceOutlineSize = 10
|
||||
emptyOutlineSize = 0
|
||||
bottomBorder = 110
|
||||
exampleCircles = 100
|
||||
w, h = 2800,2400
|
||||
backgroundColor = (230,230,234,255)
|
||||
boardOutlineColor = (0,0,0)
|
||||
pieceOutlineColor = (244,244,248)
|
||||
emptyOutlineColor = (0,0,0)
|
||||
player1Color = (254,74,73)
|
||||
player2Color = (254,215,102)
|
||||
boardColor = (42,183,202)
|
||||
placeSize = 300
|
||||
white = (255,255,255,160)
|
||||
winBarColor = (250,250,250,255)
|
||||
|
||||
fnt = ImageFont.truetype('resources/fonts/futura-bold.ttf', exampleCircles)
|
||||
|
||||
boardSize = [w-(2*(border+gridBorder)),h-(2*(border+gridBorder))]
|
||||
placeGridSize = [math.floor(boardSize[0]/7),math.floor(boardSize[1]/6)]
|
||||
pieceStartx = (border+gridBorder)+math.floor(placeGridSize[0]/2)-math.floor(placeSize/2)
|
||||
pieceStarty = (border+gridBorder)+math.floor(placeGridSize[1]/2)-math.floor(placeSize/2)
|
||||
|
||||
if game["players"][0] == "Gwendolyn":
|
||||
player1 = "Gwendolyn"
|
||||
else:
|
||||
player1 = self.bot.databaseFuncs.getName(game["players"][0])
|
||||
|
||||
if game["players"][1] == "Gwendolyn":
|
||||
player2 = "Gwendolyn"
|
||||
else:
|
||||
player2 = self.bot.databaseFuncs.getName(game["players"][1])
|
||||
|
||||
|
||||
background = Image.new("RGB", (w,h+bottomBorder),backgroundColor)
|
||||
d = ImageDraw.Draw(background,"RGBA")
|
||||
|
||||
# This whole part was the easiest way to make a rectangle with rounded corners and an outline
|
||||
# - Corners:
|
||||
d.ellipse([(border,border),(border+cornerSize,border+cornerSize)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize)
|
||||
d.ellipse([(w-(border+cornerSize),h-(border+cornerSize)),(w-border,h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize)
|
||||
d.ellipse([(border,h-(border+cornerSize)),(border+cornerSize,h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize)
|
||||
d.ellipse([(w-(border+cornerSize),border),(w-border,border+cornerSize)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize)
|
||||
# - Rectangle:
|
||||
d.rectangle([(border+math.floor(cornerSize/2),border),(w-(border+math.floor(cornerSize/2)),h-border)],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize)
|
||||
d.rectangle([(border,border+math.floor(cornerSize/2)),(w-border,h-(border+math.floor(cornerSize/2)))],fill=boardColor,outline=boardOutlineColor,width=boardOutlineSize)
|
||||
# - Removing outline on the inside:
|
||||
d.rectangle([(border+math.floor(cornerSize/2),border+math.floor(cornerSize/2)),(w-(border+math.floor(cornerSize/2)),h-(border+math.floor(cornerSize/2)))],fill=boardColor)
|
||||
d.rectangle([(border+math.floor(cornerSize/2),border+boardOutlineSize),(w-(border+math.floor(cornerSize/2)),h-(border+boardOutlineSize))],fill=boardColor)
|
||||
d.rectangle([(border+boardOutlineSize,border+math.floor(cornerSize/2)),(w-(border+boardOutlineSize),h-(border+math.floor(cornerSize/2)))],fill=boardColor)
|
||||
|
||||
for x, line in enumerate(board):
|
||||
for y, piece in enumerate(line):
|
||||
|
||||
if piece == 1:
|
||||
pieceColor = player1Color
|
||||
outlineWidth = pieceOutlineSize
|
||||
outlineColor = pieceOutlineColor
|
||||
elif piece == 2:
|
||||
pieceColor = player2Color
|
||||
outlineWidth = pieceOutlineSize
|
||||
outlineColor = pieceOutlineColor
|
||||
else:
|
||||
pieceColor = backgroundColor
|
||||
outlineWidth = emptyOutlineSize
|
||||
outlineColor = emptyOutlineColor
|
||||
|
||||
startx = pieceStartx + placeGridSize[0]*y
|
||||
starty = pieceStarty + placeGridSize[1]*x
|
||||
|
||||
d.ellipse([(startx,starty),(startx+placeSize,starty+placeSize)],fill=pieceColor,outline=outlineColor,width=outlineWidth)
|
||||
|
||||
if game["winner"] != 0:
|
||||
coordinates = game["win coordinates"]
|
||||
startx = border + placeGridSize[0]*coordinates[1] + gridBorder
|
||||
starty = border + placeGridSize[1]*coordinates[0] + gridBorder
|
||||
a = (placeGridSize[0]*4-gridBorder-border)**2
|
||||
b = (placeGridSize[1]*4-gridBorder-border)**2
|
||||
diagonalLength = (math.sqrt(a+b))/placeGridSize[0]
|
||||
diagonalAngle = math.degrees(math.atan(placeGridSize[1]/placeGridSize[0]))
|
||||
|
||||
if game["win direction"] == "h":
|
||||
winBar = Image.new("RGBA",(placeGridSize[0]*4,placeGridSize[1]),(0,0,0,0))
|
||||
winD = ImageDraw.Draw(winBar)
|
||||
winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white)
|
||||
winD.ellipse([((placeGridSize[0]*3),0),(placeGridSize[0]*4,placeGridSize[1])],fill=white)
|
||||
winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*3.5),placeGridSize[1])],fill=white)
|
||||
|
||||
elif game["win direction"] == "v":
|
||||
winBar = Image.new("RGBA",(placeGridSize[0],placeGridSize[1]*4),(0,0,0,0))
|
||||
winD = ImageDraw.Draw(winBar)
|
||||
winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white)
|
||||
winD.ellipse([(0,(placeGridSize[1]*3)),(placeGridSize[0],placeGridSize[1]*4)],fill=white)
|
||||
winD.rectangle([0,(int(placeGridSize[1]*0.5)),(placeGridSize[0],int(placeGridSize[1]*3.5))],fill=white)
|
||||
|
||||
elif game["win direction"] == "r":
|
||||
winBar = Image.new("RGBA",(int(placeGridSize[0]*diagonalLength),placeGridSize[1]),(0,0,0,0))
|
||||
winD = ImageDraw.Draw(winBar)
|
||||
winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white)
|
||||
winD.ellipse([((placeGridSize[0]*(diagonalLength-1)),0),(placeGridSize[0]*diagonalLength,placeGridSize[1])],fill=white)
|
||||
winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*(diagonalLength-0.5)),placeGridSize[1])],fill=white)
|
||||
winBar = winBar.rotate(-diagonalAngle,expand=1)
|
||||
startx -= 90
|
||||
starty -= 100
|
||||
|
||||
elif game["win direction"] == "l":
|
||||
winBar = Image.new("RGBA",(int(placeGridSize[0]*diagonalLength),placeGridSize[1]),(0,0,0,0))
|
||||
winD = ImageDraw.Draw(winBar)
|
||||
winD.ellipse([(0,0),(placeGridSize[0],placeGridSize[1])],fill=white)
|
||||
winD.ellipse([((placeGridSize[0]*(diagonalLength-1)),0),(placeGridSize[0]*diagonalLength,placeGridSize[1])],fill=white)
|
||||
winD.rectangle([(int(placeGridSize[0]*0.5),0),(int(placeGridSize[0]*(diagonalLength-0.5)),placeGridSize[1])],fill=white)
|
||||
winBar = winBar.rotate(diagonalAngle,expand=1)
|
||||
startx -= placeGridSize[0]*3 + 90
|
||||
starty -= gridBorder + 60
|
||||
|
||||
|
||||
mask = winBar.copy()#.convert("L")
|
||||
#mask.putalpha(128)
|
||||
#mask.save("test.png")
|
||||
|
||||
winBarImage = Image.new("RGBA",mask.size,color=winBarColor)
|
||||
background.paste(winBarImage,(startx,starty),mask)
|
||||
|
||||
# Bottom
|
||||
textPadding = 20
|
||||
|
||||
exampleHeight = h - border + int((bottomBorder+border)/2) - int(exampleCircles/2)
|
||||
d.ellipse([(border,exampleHeight),(border+exampleCircles),(exampleHeight+exampleCircles)],fill=player1Color,outline=boardOutlineColor,width=3)
|
||||
d.text((border+exampleCircles+textPadding,exampleHeight),player1,font=fnt,fill=(0,0,0))
|
||||
|
||||
textWidth = fnt.getsize(player2)[0]
|
||||
d.ellipse([(w-border-exampleCircles-textWidth-textPadding,exampleHeight),(w-border-textWidth-textPadding),(exampleHeight+exampleCircles)],fill=player2Color,outline=boardOutlineColor,width=3)
|
||||
d.text((w-border-textWidth,exampleHeight),player2,font=fnt,fill=(0,0,0))
|
||||
|
||||
|
||||
background.save("resources/games/connect4Boards/board"+channel+".png")
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
from .invest import Invest
|
||||
from .trivia import Trivia
|
||||
from .blackjack import Blackjack
|
||||
from .connectFour import ConnectFour
|
||||
from .hangman import Hangman
|
||||
from .hex import HexGame
|
||||
|
||||
class Games():
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
self.invest = Invest(bot)
|
||||
self.trivia = Trivia(bot)
|
||||
self.blackjack = Blackjack(bot)
|
||||
self.connectFour = ConnectFour(bot)
|
||||
self.hangman = Hangman(bot)
|
||||
self.hex = HexGame(bot)
|
||||
@@ -1,407 +0,0 @@
|
||||
import json, urllib, datetime, string, discord
|
||||
import math, random
|
||||
|
||||
from PIL import ImageDraw, Image, ImageFont
|
||||
|
||||
class Hangman():
|
||||
def __init__(self,bot):
|
||||
self.bot = bot
|
||||
self.draw = DrawHangman(bot)
|
||||
self.APIURL = "https://api.wordnik.com/v4/words.json/randomWords?hasDictionaryDef=true&minCorpusCount=5000&maxCorpusCount=-1&minDictionaryCount=1&maxDictionaryCount=-1&minLength=3&maxLength=11&limit=1&api_key="
|
||||
|
||||
async def start(self, ctx):
|
||||
await self.bot.defer(ctx)
|
||||
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
|
||||
word = "-"
|
||||
while "-" in word or "." in word:
|
||||
with urllib.request.urlopen(self.APIURL+apiKey) as p:
|
||||
word = list(json.load(p)[0]["word"].upper())
|
||||
self.bot.log("Found the word \""+"".join(word)+"\"")
|
||||
guessed = [False] * len(word)
|
||||
gameID = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
|
||||
newGame = {"_id":channel,"player" : user,"guessed letters" : [],"word" : word,"game ID" : gameID,"misses" : 0,"guessed" : guessed}
|
||||
self.bot.database["hangman games"].insert_one(newGame)
|
||||
|
||||
remainingLetters = list(string.ascii_uppercase)
|
||||
|
||||
self.draw.drawImage(channel)
|
||||
|
||||
logMessage = "Game started"
|
||||
sendMessage = f"{userName} started game of hangman."
|
||||
startedGame = True
|
||||
else:
|
||||
logMessage = "There was already a game going on"
|
||||
sendMessage = "There's already a Hangman game going on in the channel"
|
||||
|
||||
self.bot.log(logMessage)
|
||||
await ctx.send(sendMessage)
|
||||
|
||||
if startedGame:
|
||||
filePath = f"resources/games/hangmanBoards/hangmanBoard{channel}.png"
|
||||
newImage = await ctx.channel.send(file = discord.File(filePath))
|
||||
|
||||
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})
|
||||
|
||||
gameExists = (game != None)
|
||||
singleLetter = (len(guess) == 1 and guess.isalpha())
|
||||
newGuess = (guess not in game["guessed letters"])
|
||||
validGuess = (gameExists and singleLetter and newGuess)
|
||||
|
||||
if validGuess:
|
||||
self.bot.log("Guessed the letter")
|
||||
correctGuess = 0
|
||||
|
||||
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 correctGuess == 0:
|
||||
self.bot.database["hangman games"].update_one({"_id":channel},{"$inc":{"misses":1}})
|
||||
|
||||
self.bot.database["hangman games"].update_one({"_id":channel},{"$push":{"guessed letters":guess}})
|
||||
|
||||
remainingLetters = list(string.ascii_uppercase)
|
||||
|
||||
game = self.bot.database["hangman games"].find_one({"_id":channel})
|
||||
|
||||
for letter in game["guessed letters"]:
|
||||
remainingLetters.remove(letter)
|
||||
|
||||
if correctGuess == 1:
|
||||
sendMessage = f"Guessed {guess}. There was 1 {guess} in the word."
|
||||
else:
|
||||
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)
|
||||
|
||||
class DrawHangman():
|
||||
def __init__(self,bot):
|
||||
self.bot = bot
|
||||
self.CIRCLESIZE = 120
|
||||
self.LINEWIDTH = 12
|
||||
|
||||
self.BODYSIZE =210
|
||||
self.LIMBSIZE = 60
|
||||
self.ARMPOSITION = 60
|
||||
|
||||
self.MANX = (self.LIMBSIZE*2)+self.LINEWIDTH*4
|
||||
self.MANY = (self.CIRCLESIZE+self.BODYSIZE+self.LIMBSIZE)+self.LINEWIDTH*4
|
||||
|
||||
self.LETTERLINELENGTH = 90
|
||||
self.LETTERLINEDISTANCE = 30
|
||||
|
||||
self.GALLOWX, self.GALLOWY = 360,600
|
||||
self.GOLDENRATIO = 1-(1 / ((1 + 5 ** 0.5) / 2))
|
||||
|
||||
LETTERSIZE = 75
|
||||
TEXTSIZE = 70
|
||||
self.FONT = ImageFont.truetype('resources/fonts/comic-sans-bold.ttf', LETTERSIZE)
|
||||
self.SMALLFONT = ImageFont.truetype('resources/fonts/comic-sans-bold.ttf', TEXTSIZE)
|
||||
|
||||
def calcDeviance(self,preDev,preDevAcc,posChange,maxmin,maxAcceleration):
|
||||
devAcc = preDevAcc + random.uniform(-posChange,posChange)
|
||||
if devAcc > maxmin * maxAcceleration: devAcc = maxmin * maxAcceleration
|
||||
elif devAcc < -maxmin * maxAcceleration: devAcc = -maxmin * maxAcceleration
|
||||
|
||||
dev = preDev + devAcc
|
||||
if dev > maxmin: dev = maxmin
|
||||
elif dev < -maxmin: dev = -maxmin
|
||||
return dev, devAcc
|
||||
|
||||
def badCircle(self):
|
||||
background = Image.new("RGBA",(self.CIRCLESIZE+(self.LINEWIDTH*3),self.CIRCLESIZE+(self.LINEWIDTH*3)),color=(0,0,0,0))
|
||||
|
||||
d = ImageDraw.Draw(background,"RGBA")
|
||||
middle = (self.CIRCLESIZE+(self.LINEWIDTH*3))/2
|
||||
devx = 0
|
||||
devy = 0
|
||||
devAccx = 0
|
||||
devAccy = 0
|
||||
start = random.randint(-100,-80)
|
||||
degreesAmount = 360 + random.randint(-10,30)
|
||||
|
||||
for degree in range(degreesAmount):
|
||||
devx, devAccx = self.calcDeviance(devx,devAccx,self.LINEWIDTH/100,self.LINEWIDTH,0.03)
|
||||
devy, devAccy = self.calcDeviance(devy,devAccy,self.LINEWIDTH/100,self.LINEWIDTH,0.03)
|
||||
|
||||
x = middle + (math.cos(math.radians(degree+start)) * (self.CIRCLESIZE/2)) - (self.LINEWIDTH/2) + devx
|
||||
y = middle + (math.sin(math.radians(degree+start)) * (self.CIRCLESIZE/2)) - (self.LINEWIDTH/2) + devy
|
||||
|
||||
d.ellipse([(x,y),(x+self.LINEWIDTH,y+self.LINEWIDTH)],fill=(0,0,0,255))
|
||||
|
||||
return background
|
||||
|
||||
def badLine(self, length, rotated = False):
|
||||
if rotated:
|
||||
w, h = length+self.LINEWIDTH*3, self.LINEWIDTH*3
|
||||
else:
|
||||
w, h = self.LINEWIDTH*3,length+self.LINEWIDTH*3
|
||||
background = Image.new("RGBA",(w,h),color=(0,0,0,0))
|
||||
|
||||
d = ImageDraw.Draw(background,"RGBA")
|
||||
devx = random.randint(-int(self.LINEWIDTH/3),int(self.LINEWIDTH/3))
|
||||
devy = 0
|
||||
devAccx = 0
|
||||
devAccy = 0
|
||||
|
||||
for pixel in range(length):
|
||||
devx, devAccx = self.calcDeviance(devx,devAccx,self.LINEWIDTH/1000,self.LINEWIDTH,0.004)
|
||||
devy, devAccy = self.calcDeviance(devy,devAccy,self.LINEWIDTH/1000,self.LINEWIDTH,0.004)
|
||||
|
||||
if rotated:
|
||||
x = self.LINEWIDTH + pixel + devx
|
||||
y = self.LINEWIDTH + devy
|
||||
else:
|
||||
x = self.LINEWIDTH + devx
|
||||
y = self.LINEWIDTH + pixel + devy
|
||||
|
||||
d.ellipse([(x,y),(x+self.LINEWIDTH,y+self.LINEWIDTH)],fill=(0,0,0,255))
|
||||
|
||||
return background
|
||||
|
||||
def drawMan(self, misses):
|
||||
background = Image.new("RGBA",(self.MANX,self.MANY),color=(0,0,0,0))
|
||||
|
||||
if misses >= 1:
|
||||
head = self.badCircle()
|
||||
background.paste(head,(int((self.MANX-(self.CIRCLESIZE+(self.LINEWIDTH*3)))/2),0),head)
|
||||
if misses >= 2:
|
||||
body = self.badLine(self.BODYSIZE)
|
||||
background.paste(body,(int((self.MANX-(self.LINEWIDTH*3))/2),self.CIRCLESIZE),body)
|
||||
|
||||
if misses >= 3:
|
||||
limbs = random.sample(["rl","ll","ra","la"],min(misses-2,4))
|
||||
else: limbs = []
|
||||
|
||||
for limb in limbs:
|
||||
if limb == "ra":
|
||||
limbDrawing = self.badLine(self.LIMBSIZE,True)
|
||||
rotation = random.randint(-45,45)
|
||||
xpos = int((self.MANX-(self.LINEWIDTH*3))/2)
|
||||
rotationCompensation = min(-int(math.sin(math.radians(rotation))*(self.LIMBSIZE+(self.LINEWIDTH*3))),0)
|
||||
ypos = self.CIRCLESIZE+self.ARMPOSITION + rotationCompensation
|
||||
limbDrawing = limbDrawing.rotate(rotation,expand=1)
|
||||
background.paste(limbDrawing,(xpos,ypos),limbDrawing)
|
||||
elif limb == "la":
|
||||
limbDrawing = self.badLine(self.LIMBSIZE,True)
|
||||
rotation = random.randint(-45,45)
|
||||
xpos = int((self.MANX-(self.LINEWIDTH*3))/2)-self.LIMBSIZE
|
||||
rotationCompensation = min(int(math.sin(math.radians(rotation))*(self.LIMBSIZE+(self.LINEWIDTH*3))),0)
|
||||
ypos = self.CIRCLESIZE+self.ARMPOSITION + rotationCompensation
|
||||
limbDrawing = limbDrawing.rotate(rotation,expand=1)
|
||||
background.paste(limbDrawing,(xpos,ypos),limbDrawing)
|
||||
elif limb == "rl":
|
||||
limbDrawing = self.badLine(self.LIMBSIZE,True)
|
||||
rotation = random.randint(-15,15)
|
||||
xpos = int((self.MANX-(self.LINEWIDTH*3))/2)-self.LINEWIDTH
|
||||
ypos = self.CIRCLESIZE+self.BODYSIZE-self.LINEWIDTH
|
||||
limbDrawing = limbDrawing.rotate(rotation-45,expand=1)
|
||||
background.paste(limbDrawing,(xpos,ypos),limbDrawing)
|
||||
elif limb == "ll":
|
||||
limbDrawing = self.badLine(self.LIMBSIZE,True)
|
||||
rotation = random.randint(-15,15)
|
||||
limbDrawing = limbDrawing.rotate(rotation+45,expand=1)
|
||||
xpos = int((self.MANX-(self.LINEWIDTH*3))/2)-limbDrawing.size[0]+self.LINEWIDTH*3
|
||||
ypos = self.CIRCLESIZE+self.BODYSIZE
|
||||
background.paste(limbDrawing,(xpos,ypos),limbDrawing)
|
||||
|
||||
return background
|
||||
|
||||
def badText(self, text, big, color=(0,0,0,255)):
|
||||
if big: font = self.FONT
|
||||
else: font = self.SMALLFONT
|
||||
w, h = font.getsize(text)
|
||||
img = Image.new("RGBA",(w,h),color=(0,0,0,0))
|
||||
d = ImageDraw.Draw(img,"RGBA")
|
||||
|
||||
d.text((0,0),text,font=font,fill=color)
|
||||
return img
|
||||
|
||||
def drawGallows(self):
|
||||
background = Image.new("RGBA",(self.GALLOWX,self.GALLOWY),color=(0,0,0,0))
|
||||
|
||||
bottomLine = self.badLine(int(self.GALLOWX*0.75),True)
|
||||
background.paste(bottomLine,(int(self.GALLOWX*0.125),self.GALLOWY-(self.LINEWIDTH*4)),bottomLine)
|
||||
|
||||
lineTwo = self.badLine(self.GALLOWY-self.LINEWIDTH*6)
|
||||
background.paste(lineTwo,(int(self.GALLOWX*(0.75*self.GOLDENRATIO)),self.LINEWIDTH*2),lineTwo)
|
||||
|
||||
topLine = self.badLine(int(self.GALLOWY*0.30),True)
|
||||
background.paste(topLine,(int(self.GALLOWX*(0.75*self.GOLDENRATIO))-self.LINEWIDTH,self.LINEWIDTH*3),topLine)
|
||||
|
||||
lastLine = self.badLine(int(self.GALLOWY*0.125))
|
||||
background.paste(lastLine,((int(self.GALLOWX*(0.75*self.GOLDENRATIO))+int(self.GALLOWY*0.30)-self.LINEWIDTH),self.LINEWIDTH*3),lastLine)
|
||||
return background
|
||||
|
||||
def drawLetterLines(self, word,guessed,misses):
|
||||
letterLines = Image.new("RGBA",((self.LETTERLINELENGTH+self.LETTERLINEDISTANCE)*len(word),self.LETTERLINELENGTH+self.LINEWIDTH*3),color=(0,0,0,0))
|
||||
for x, letter in enumerate(word):
|
||||
line = self.badLine(self.LETTERLINELENGTH,True)
|
||||
letterLines.paste(line,(x*(self.LETTERLINELENGTH+self.LETTERLINEDISTANCE),self.LETTERLINELENGTH),line)
|
||||
if guessed[x]:
|
||||
letterDrawing = self.badText(letter,True)
|
||||
letterWidth = self.FONT.getsize(letter)[0]
|
||||
letterx = int(x*(self.LETTERLINELENGTH+self.LETTERLINEDISTANCE)-(letterWidth/2)+(self.LETTERLINELENGTH*0.5)+(self.LINEWIDTH*2))
|
||||
letterLines.paste(letterDrawing,(letterx,0),letterDrawing)
|
||||
elif misses == 6:
|
||||
letterDrawing = self.badText(letter,True,(242,66,54))
|
||||
letterWidth = self.FONT.getsize(letter)[0]
|
||||
letterx = int(x*(self.LETTERLINELENGTH+self.LETTERLINEDISTANCE)-(letterWidth/2)+(self.LETTERLINELENGTH*0.5)+(self.LINEWIDTH*2))
|
||||
letterLines.paste(letterDrawing,(letterx,0),letterDrawing)
|
||||
|
||||
return letterLines
|
||||
|
||||
def shortestDist(self,positions,newPosition):
|
||||
shortestDist = math.inf
|
||||
x, y = newPosition
|
||||
for i, j in positions:
|
||||
xdist = abs(i-x)
|
||||
ydist = abs(j-y)
|
||||
dist = math.sqrt(xdist**2+ydist**2)
|
||||
if shortestDist > dist: shortestDist = dist
|
||||
return shortestDist
|
||||
|
||||
def drawMisses(self,guesses,word):
|
||||
background = Image.new("RGBA",(600,400),color=(0,0,0,0))
|
||||
pos = []
|
||||
for guess in guesses:
|
||||
if guess not in word:
|
||||
placed = False
|
||||
while placed == False:
|
||||
letter = self.badText(guess,True)
|
||||
w, h = self.FONT.getsize(guess)
|
||||
x = random.randint(0,600-w)
|
||||
y = random.randint(0,400-h)
|
||||
if self.shortestDist(pos,(x,y)) > 70:
|
||||
pos.append((x,y))
|
||||
background.paste(letter,(x,y),letter)
|
||||
placed = True
|
||||
return background
|
||||
|
||||
def drawImage(self,channel):
|
||||
self.bot.log("Drawing hangman image", channel)
|
||||
game = self.bot.database["hangman games"].find_one({"_id":channel})
|
||||
|
||||
random.seed(game["game ID"])
|
||||
|
||||
background = Image.open("resources/paper.jpg")
|
||||
try:
|
||||
gallow = self.drawGallows()
|
||||
except:
|
||||
self.bot.log("Error drawing gallows (error code 1711)")
|
||||
|
||||
try:
|
||||
man = self.drawMan(game["misses"])
|
||||
except:
|
||||
self.bot.log("Error drawing stick figure (error code 1712)")
|
||||
|
||||
random.seed(game["game ID"])
|
||||
try:
|
||||
letterLines = self.drawLetterLines(game["word"],game["guessed"],game["misses"])
|
||||
except:
|
||||
self.bot.log("error drawing letter lines (error code 1713)")
|
||||
|
||||
random.seed(game["game ID"])
|
||||
try:
|
||||
misses = self.drawMisses(game["guessed letters"],game["word"])
|
||||
except:
|
||||
self.bot.log("Error drawing misses (error code 1714)")
|
||||
|
||||
background.paste(gallow,(100,100),gallow)
|
||||
background.paste(man,(300,210),man)
|
||||
background.paste(letterLines,(120,840),letterLines)
|
||||
background.paste(misses,(600,150),misses)
|
||||
|
||||
missesText = self.badText("MISSES",False)
|
||||
missesTextWidth = missesText.size[0]
|
||||
background.paste(missesText,(850-int(missesTextWidth/2),50),missesText)
|
||||
|
||||
background.save("resources/games/hangmanBoards/hangmanBoard"+channel+".png")
|
||||
@@ -1,149 +0,0 @@
|
||||
import discord
|
||||
|
||||
class Invest():
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
def getPrice(self, symbol : str):
|
||||
res = self.bot.finnhubClient.quote(symbol.upper())
|
||||
if res == {}:
|
||||
return 0
|
||||
else:
|
||||
return int(res["c"] * 100)
|
||||
|
||||
def getPortfolio(self, user : str):
|
||||
userInvestments = self.bot.database["investments"].find_one({"_id":user})
|
||||
|
||||
if userInvestments in [None,{}]:
|
||||
return f"{self.bot.databaseFuncs.getName(user)} does not have a stock portfolio."
|
||||
else:
|
||||
portfolio = f"**Stock portfolio for {self.bot.databaseFuncs.getName(user)}**"
|
||||
|
||||
for key, value in list(userInvestments["investments"].items()):
|
||||
purchaseValue = value["purchased for"]
|
||||
currentValue = int((self.getPrice(key) / value["value at purchase"]) * value["purchased"])
|
||||
if purchaseValue == "?":
|
||||
portfolio += f"\n**{key}**: ___{str(currentValue)} GwendoBucks___"
|
||||
else:
|
||||
portfolio += f"\n**{key}**: ___{str(currentValue)} GwendoBucks___ (purchased for {str(purchaseValue)})"
|
||||
|
||||
return portfolio
|
||||
|
||||
def buyStock(self, user : str, stock : str, buyAmount : int):
|
||||
if buyAmount >= 100:
|
||||
if self.bot.money.checkBalance(user) >= buyAmount:
|
||||
stockPrice = self.getPrice(stock)
|
||||
if stockPrice > 0:
|
||||
userInvestments = self.bot.database["investments"].find_one({"_id":user})
|
||||
|
||||
self.bot.money.addMoney(user,-1*buyAmount)
|
||||
stock = stock.upper()
|
||||
|
||||
if userInvestments != None:
|
||||
userInvestments = userInvestments["investments"]
|
||||
if stock in userInvestments:
|
||||
value = userInvestments[stock]
|
||||
newAmount = int((stockPrice / value["value at purchase"]) * value["purchased"]) + buyAmount
|
||||
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$set":{"investments."+stock+".value at purchase" : stockPrice}})
|
||||
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$set":{"investments."+stock+".purchased" : newAmount}})
|
||||
|
||||
if value["purchased for"] != "?":
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$set":{"investments."+stock+".purchased for" : buyAmount}})
|
||||
else:
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$set":{"investments."+stock : {"purchased" : buyAmount, "value at purchase" : stockPrice,
|
||||
"purchased for" : buyAmount}}})
|
||||
else:
|
||||
newUser = {"_id":user,"investments":{stock : {"purchased" : buyAmount, "value at purchase" : stockPrice, "purchased for" : buyAmount}}}
|
||||
self.bot.database["investments"].insert_one(newUser)
|
||||
|
||||
return f"{self.bot.databaseFuncs.getName(user)} bought {buyAmount} GwendoBucks worth of {stock} stock"
|
||||
else:
|
||||
return f"{stock} is not traded on the american market."
|
||||
else:
|
||||
return "You don't have enough money for that"
|
||||
else:
|
||||
return "You cannot buy stocks for less than 100 GwendoBucks"
|
||||
|
||||
def sellStock(self, user : str, stock : str, sellAmount : int):
|
||||
if sellAmount > 0:
|
||||
|
||||
userInvestments = self.bot.database["investments"].find_one({"_id":user})["investments"]
|
||||
|
||||
stock = stock.upper()
|
||||
|
||||
if userInvestments != None and stock in userInvestments:
|
||||
value = userInvestments[stock]
|
||||
stockPrice = self.getPrice(stock)
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$set":{"investments."+stock+".purchased" :
|
||||
int((stockPrice / value["value at purchase"]) * value["purchased"])}})
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$set":{"investments."+stock+".value at purchase" : stockPrice}})
|
||||
if value["purchased"] >= sellAmount:
|
||||
self.bot.money.addMoney(user,sellAmount)
|
||||
if sellAmount < value["purchased"]:
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$inc":{"investments."+stock+".purchased" : -sellAmount}})
|
||||
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$set":{"investments."+stock+".purchased for" : "?"}})
|
||||
else:
|
||||
self.bot.database["investments"].update_one({"_id":user},
|
||||
{"$unset":{"investments."+stock:""}})
|
||||
|
||||
return f"{self.bot.databaseFuncs.getName(user)} sold {sellAmount} GwendoBucks worth of {stock} stock"
|
||||
else:
|
||||
return f"You don't have enough {stock} stocks to do that"
|
||||
else:
|
||||
return f"You don't have any {stock} stock"
|
||||
else:
|
||||
return "no"
|
||||
|
||||
async def parseInvest(self, ctx, parameters):
|
||||
await self.bot.defer(ctx)
|
||||
user = f"#{ctx.author.id}"
|
||||
|
||||
if parameters.startswith("check"):
|
||||
commands = parameters.split(" ")
|
||||
if len(commands) == 1:
|
||||
response = self.getPortfolio(user)
|
||||
else:
|
||||
price = self.getPrice(commands[1])
|
||||
if price == 0:
|
||||
response = f"{commands[1].upper()} is not traded on the american market."
|
||||
else:
|
||||
price = f"{price:,}".replace(",",".")
|
||||
response = f"The current {commands[1].upper()} stock is valued at **{price}** GwendoBucks"
|
||||
|
||||
elif parameters.startswith("buy"):
|
||||
commands = parameters.split(" ")
|
||||
if len(commands) == 3:
|
||||
response = self.buyStock(user,commands[1],int(commands[2]))
|
||||
else:
|
||||
response = "You must give both a stock name and an amount of gwendobucks you wish to spend."
|
||||
|
||||
elif parameters.startswith("sell"):
|
||||
commands = parameters.split(" ")
|
||||
if len(commands) == 3:
|
||||
try:
|
||||
response = self.sellStock(user,commands[1],int(commands[2]))
|
||||
except:
|
||||
response = "The command must be given as \"/invest sell [stock] [amount of GwendoBucks to sell stocks for]\""
|
||||
else:
|
||||
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)
|
||||
@@ -1,70 +0,0 @@
|
||||
class Money():
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.database = bot.database
|
||||
|
||||
# Returns the account balance for a user
|
||||
def checkBalance(self, user):
|
||||
self.bot.log("checking "+user+"'s account balance")
|
||||
|
||||
userData = self.database["users"].find_one({"_id":user})
|
||||
|
||||
if userData != None:
|
||||
return userData["money"]
|
||||
else: return 0
|
||||
|
||||
async def sendBalance(self, ctx):
|
||||
await self.bot.defer(ctx)
|
||||
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")
|
||||
|
||||
userData = self.database["users"].find_one({"_id":user})
|
||||
|
||||
if userData != None:
|
||||
self.database["users"].update_one({"_id":user},{"$inc":{"money":amount}})
|
||||
else:
|
||||
self.database["users"].insert_one({"_id":user,"user name":self.bot.databaseFuncs.getName(user),"money":amount})
|
||||
|
||||
# Transfers money from one user to another
|
||||
async def giveMoney(self, ctx, user, amount):
|
||||
await self.bot.defer(ctx)
|
||||
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(f"#{ctx.author.id}",-1 * amount)
|
||||
self.addMoney(targetUser,amount)
|
||||
await ctx.send(f"Transferred {amount} GwendoBucks to {username}")
|
||||
else:
|
||||
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")
|
||||
await ctx.send("You don't have that many GwendoBuck")
|
||||
else:
|
||||
self.bot.log("They weren't in the system")
|
||||
await ctx.send("The target doesn't exist")
|
||||
else:
|
||||
self.bot.log("They tried to steal")
|
||||
await ctx.send("Yeah, no. You can't do that")
|
||||
@@ -1,118 +0,0 @@
|
||||
import json
|
||||
import urllib
|
||||
import random
|
||||
import asyncio
|
||||
|
||||
class Trivia():
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
# Starts a game of trivia. Downloads a question with answers, shuffles the wrong answers with the
|
||||
# correct answer and returns the questions and answers. Also saves the question in the games.json file.
|
||||
def triviaStart(self, channel : str):
|
||||
question = self.bot.database["trivia questions"].find_one({"_id":channel})
|
||||
|
||||
self.bot.log("Trying to find a trivia question for "+channel)
|
||||
|
||||
if question == None:
|
||||
with urllib.request.urlopen("https://opentdb.com/api.php?amount=10&type=multiple") as response:
|
||||
data = json.loads(response.read())
|
||||
|
||||
self.bot.log("Found the question \""+data["results"][0]["question"]+"\"")
|
||||
answers = data["results"][0]["incorrect_answers"]
|
||||
answers.append(data["results"][0]["correct_answer"])
|
||||
random.shuffle(answers)
|
||||
correctAnswer = answers.index(data["results"][0]["correct_answer"]) + 97
|
||||
|
||||
self.bot.database["trivia questions"].insert_one({"_id":channel,"answer" : str(chr(correctAnswer)),"players" : {}})
|
||||
|
||||
replacements = {"'": "\'",
|
||||
""": "\"",
|
||||
"“": "\"",
|
||||
"”": "\"",
|
||||
"é": "é"}
|
||||
question = data["results"][0]["question"]
|
||||
|
||||
for key, value in replacements.items():
|
||||
question = question.replace(key,value)
|
||||
|
||||
for answer in answers:
|
||||
for key, value in replacements.items():
|
||||
answer = answer.replace(key,value)
|
||||
|
||||
return question, answers, correctAnswer
|
||||
else:
|
||||
self.bot.log("There was already a trivia question for that channel (error code 1106)")
|
||||
return "There's already a trivia question going on. Try again in like, a minute (error code 1106)", "", ""
|
||||
|
||||
# Lets players answer a trivia question
|
||||
def triviaAnswer(self, user : str, channel : str, command : str):
|
||||
question = self.bot.database["trivia questions"].find_one({"_id":channel})
|
||||
|
||||
if command in ["a","b","c","d"]:
|
||||
if question != None:
|
||||
if user not in question["players"]:
|
||||
self.bot.log(user+" answered the question in "+channel)
|
||||
|
||||
self.bot.database["trivia questions"].update_one({"_id":channel},{"$set":{"players."+user : command}})
|
||||
|
||||
return "Locked in "+user+"'s answer"
|
||||
else:
|
||||
self.bot.log(user+" has already answered this question (error code 1105)")
|
||||
return user+" has already answered this question (error code 1105)"
|
||||
else:
|
||||
self.bot.log("There's no question right now (error code 1104)")
|
||||
return "There's no question right now (error code 1104)"
|
||||
else:
|
||||
self.bot.log("I didn't quite understand that (error code 1103)")
|
||||
return "I didn't quite understand that (error code 1103)"
|
||||
|
||||
|
||||
# Adds 1 GwendoBuck to each player that got the question right and deletes question from games.json.
|
||||
def triviaCountPoints(self, channel : str):
|
||||
question = self.bot.database["trivia questions"].find_one({"_id":channel})
|
||||
|
||||
self.bot.log("Counting points for question in "+channel)
|
||||
|
||||
if question != None:
|
||||
for player, answer in question["players"].items():
|
||||
if answer == question["answer"]:
|
||||
self.bot.money.addMoney(player,1)
|
||||
|
||||
|
||||
else:
|
||||
self.bot.log("Couldn't find the question (error code 1102)")
|
||||
|
||||
return None
|
||||
|
||||
async def triviaParse(self, ctx, answer):
|
||||
await self.bot.defer(ctx)
|
||||
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)")
|
||||
@@ -1,413 +0,0 @@
|
||||
import requests, imdb, discord, json, math, time, asyncio
|
||||
|
||||
class BedreNetflix():
|
||||
def __init__(self,bot):
|
||||
self.bot = bot
|
||||
ip = ["localhost", "192.168.0.40"][self.bot.options.testing]
|
||||
|
||||
self.radarrURL = "http://"+ip+":7878/api/v3/"
|
||||
self.sonarrURL = "http://"+ip+":8989/api/"
|
||||
self.qbittorrentURL = "http://"+ip+":8080/api/v2/"
|
||||
self.moviePath = "/media/plex/Server/movies/"
|
||||
self.showPath = "/media/plex/Server/Shows/"
|
||||
|
||||
#Returns a list of no more than 5 options when user requests a movie
|
||||
async def requestMovie(self, ctx, movieName):
|
||||
await self.bot.defer(ctx)
|
||||
|
||||
self.bot.log("Searching for "+movieName)
|
||||
movieList = imdb.IMDb().search_movie(movieName)
|
||||
movies = []
|
||||
for movie in movieList:
|
||||
if movie["kind"] == "movie":
|
||||
movies.append(movie)
|
||||
if len(movies) > 5:
|
||||
movies = movies[:5]
|
||||
|
||||
if len(movies) == 1:
|
||||
messageTitle = "**Is it this movie?**"
|
||||
else:
|
||||
messageTitle = "**Is it any of these movies?**"
|
||||
|
||||
messageText = ""
|
||||
imdbIds = []
|
||||
|
||||
for x, movie in enumerate(movies):
|
||||
try:
|
||||
messageText += "\n"+str(x+1)+") "+movie["title"]+" ("+str(movie["year"])+")"
|
||||
except:
|
||||
try:
|
||||
messageText += "\n"+str(x+1)+") "+movie["title"]
|
||||
except:
|
||||
messageText += "Error"
|
||||
imdbIds.append(movie.movieID)
|
||||
|
||||
self.bot.log("Returning a list of "+str(len(movies))+" possible movies: "+str(imdbIds))
|
||||
|
||||
em = discord.Embed(title=messageTitle,description=messageText,colour=0x00FF00)
|
||||
|
||||
message = await ctx.send(embed=em)
|
||||
|
||||
messageData = {"messageID":message.id,"imdbIds":imdbIds}
|
||||
|
||||
with open("resources/bedreNetflix/oldMessage"+str(ctx.channel.id),"w") as f:
|
||||
json.dump(messageData,f)
|
||||
|
||||
if len(movies) == 1:
|
||||
await message.add_reaction("✔️")
|
||||
else:
|
||||
for x in range(len(movies)):
|
||||
await message.add_reaction(["1️⃣","2️⃣","3️⃣","4️⃣","5️⃣"][x])
|
||||
|
||||
await message.add_reaction("❌")
|
||||
|
||||
message = await ctx.channel.fetch_message(message.id)
|
||||
if message.content != "" and not isinstance(ctx.channel, discord.DMChannel):
|
||||
await message.clear_reactions()
|
||||
|
||||
#Adds the requested movie to Bedre Netflix
|
||||
async def addMovie(self, message, imdbId, editMessage = True):
|
||||
if imdbId == None:
|
||||
self.bot.log("Did not find what the user was searching for")
|
||||
if editMessage:
|
||||
await message.edit(embed = None, content = "Try searching for the IMDB id")
|
||||
else:
|
||||
await message.channel.send("Try searching for the IMDB id")
|
||||
else:
|
||||
self.bot.log("Trying to add movie "+str(imdbId))
|
||||
apiKey = self.bot.credentials.radarrKey
|
||||
response = requests.get(self.radarrURL+"movie/lookup/imdb?imdbId=tt"+imdbId+"&apiKey="+apiKey)
|
||||
lookupData = response.json()
|
||||
postData = {"qualityProfileId": 1,
|
||||
"rootFolderPath" : self.moviePath,
|
||||
"monitored" : True,
|
||||
"addOptions": {"searchForMovie": True}}
|
||||
for key in ["tmdbId","title","titleSlug","images","year"]:
|
||||
postData.update({key : lookupData[key]})
|
||||
|
||||
r = requests.post(url= self.radarrURL+"movie?apikey="+apiKey,json = postData)
|
||||
|
||||
if r.status_code == 201:
|
||||
if editMessage:
|
||||
await message.edit(embed = None, content = postData["title"]+" successfully added to Bedre Netflix")
|
||||
else:
|
||||
await message.channel.send(postData["title"]+" successfully added to Bedre Netflix")
|
||||
|
||||
self.bot.log("Added "+postData["title"]+" to Bedre Netflix")
|
||||
elif r.status_code == 400:
|
||||
text = f"{postData['title']} is either already on Bedre Netflix, downloading, or not available"
|
||||
if editMessage:
|
||||
await message.edit(embed = None, content = text)
|
||||
else:
|
||||
await message.channel.send(text)
|
||||
else:
|
||||
if editMessage:
|
||||
await message.edit(embed = None, content = "Something went wrong")
|
||||
else:
|
||||
await message.channel.send("Something went wrong")
|
||||
self.bot.log(str(r.status_code)+" "+r.reason)
|
||||
|
||||
#Returns a list of no more than 5 options when user requests a show
|
||||
async def requestShow(self, ctx, showName):
|
||||
await self.bot.defer(ctx)
|
||||
|
||||
self.bot.log("Searching for "+showName)
|
||||
movies = imdb.IMDb().search_movie(showName) #Replace with tvdb
|
||||
shows = []
|
||||
for movie in movies:
|
||||
if movie["kind"] in ["tv series","tv miniseries"]:
|
||||
shows.append(movie)
|
||||
if len(shows) > 5:
|
||||
shows = shows[:5]
|
||||
|
||||
if len(shows) == 1:
|
||||
messageTitle = "**Is it this show?**"
|
||||
else:
|
||||
messageTitle = "**Is it any of these shows?**"
|
||||
|
||||
messageText = ""
|
||||
imdbNames = []
|
||||
|
||||
for x, show in enumerate(shows):
|
||||
try:
|
||||
messageText += "\n"+str(x+1)+") "+show["title"]+" ("+str(show["year"])+")"
|
||||
except:
|
||||
try:
|
||||
messageText += "\n"+str(x+1)+") "+show["title"]
|
||||
except:
|
||||
messageText += "Error"
|
||||
imdbNames.append(show["title"])
|
||||
|
||||
self.bot.log("Returning a list of "+str(len(shows))+" possible shows: "+str(imdbNames))
|
||||
|
||||
em = discord.Embed(title=messageTitle,description=messageText,colour=0x00FF00)
|
||||
|
||||
message = await ctx.send(embed=em)
|
||||
|
||||
messageData = {"messageID":message.id,"imdbNames":imdbNames}
|
||||
|
||||
with open("resources/bedreNetflix/oldMessage"+str(ctx.channel.id),"w") as f:
|
||||
json.dump(messageData,f)
|
||||
|
||||
if len(shows) == 1:
|
||||
await message.add_reaction("✔️")
|
||||
else:
|
||||
for x in range(len(shows)):
|
||||
await message.add_reaction(["1️⃣","2️⃣","3️⃣","4️⃣","5️⃣"][x])
|
||||
|
||||
await message.add_reaction("❌")
|
||||
|
||||
message = await ctx.channel.fetch_message(message.id)
|
||||
if message.content != "" and not isinstance(ctx.channel, discord.DMChannel):
|
||||
await message.clear_reactions()
|
||||
|
||||
#Adds the requested show to Bedre Netflix
|
||||
async def addShow(self, message, imdbName):
|
||||
if imdbName == None:
|
||||
self.bot.log("Did not find what the user was searching for")
|
||||
await message.edit(embed = None, content = "Try searching for the IMDB id")
|
||||
else:
|
||||
self.bot.log("Trying to add show "+str(imdbName))
|
||||
apiKey = self.bot.credentials.sonarrKey
|
||||
response = requests.get(self.sonarrURL+"series/lookup?term="+imdbName.replace(" ","%20")+"&apiKey="+apiKey)
|
||||
lookupData = response.json()[0]
|
||||
postData = {"ProfileId" : 1,
|
||||
"rootFolderPath" : self.showPath,
|
||||
"monitored" : True,
|
||||
"addOptions" : {"searchForMissingEpisodes" : True}}
|
||||
for key in ["tvdbId","title","titleSlug","images","seasons"]:
|
||||
postData.update({key : lookupData[key]})
|
||||
|
||||
r = requests.post(url= self.sonarrURL+"series?apikey="+apiKey,json = postData)
|
||||
|
||||
if r.status_code == 201:
|
||||
await message.edit(embed = None, content = postData["title"]+" successfully added to Bedre Netflix")
|
||||
self.bot.log("Added a "+postData["title"]+" to Bedre Netflix")
|
||||
elif r.status_code == 400:
|
||||
text = f"{postData['title']} is either already on Bedre Netflix, downloading, or not available"
|
||||
await message.edit(embed = None, content = text)
|
||||
else:
|
||||
await message.edit(embed = None, content = "Something went wrong")
|
||||
self.bot.log(str(r.status_code)+" "+r.reason)
|
||||
|
||||
#Generates a list of all torrents and returns formatted list and whether all torrents are downloaded
|
||||
async def genDownloadList(self, showDM, showMovies, showShows, episodes):
|
||||
self.bot.log("Generating torrent list")
|
||||
titleWidth = 100
|
||||
message = []
|
||||
allDownloaded = True
|
||||
|
||||
if showDM:
|
||||
message.append("")
|
||||
DMSectionTitle = "*Torrent Downloads*"
|
||||
DMSectionTitleLine = "-"*((titleWidth-len(DMSectionTitle))//2)
|
||||
message.append(DMSectionTitleLine+DMSectionTitle+DMSectionTitleLine)
|
||||
response = requests.get(self.qbittorrentURL+"torrents/info")
|
||||
torrentList = response.json()
|
||||
|
||||
if len(torrentList) > 0:
|
||||
for torrent in torrentList:
|
||||
torrentName = torrent["name"]
|
||||
if len(torrentName) > 30:
|
||||
if torrentName[26] == " ":
|
||||
torrentName = torrentName[:26]+"...."
|
||||
else:
|
||||
torrentName = torrentName[:27]+"..."
|
||||
while len(torrentName) < 30:
|
||||
torrentName += " "
|
||||
|
||||
if torrent["size"] == 0:
|
||||
downloadedRatio = 0
|
||||
elif torrent["amount_left"] == 0:
|
||||
downloadedRatio = 1
|
||||
else:
|
||||
downloadedRatio = min(torrent["downloaded"]/torrent["size"],1)
|
||||
progressBar = "|"+("█"*math.floor(downloadedRatio*20))
|
||||
while len(progressBar) < 21:
|
||||
progressBar += " "
|
||||
|
||||
progressBar += "| "+str(math.floor(downloadedRatio*100))+"%"
|
||||
|
||||
while len(progressBar) < 27:
|
||||
progressBar += " "
|
||||
|
||||
etaInSeconds = torrent["eta"]
|
||||
|
||||
if etaInSeconds >= 8640000:
|
||||
eta = "∞"
|
||||
else:
|
||||
eta = ""
|
||||
if etaInSeconds >= 86400:
|
||||
eta += str(math.floor(etaInSeconds/86400))+"d "
|
||||
if etaInSeconds >= 3600:
|
||||
eta += str(math.floor((etaInSeconds%86400)/3600))+"h "
|
||||
if etaInSeconds >= 60:
|
||||
eta += str(math.floor((etaInSeconds%3600)/60))+"m "
|
||||
|
||||
eta += str(etaInSeconds%60)+"s"
|
||||
|
||||
torrentInfo = torrentName+" "+progressBar+" (Eta: "+eta+")"
|
||||
|
||||
if torrent["state"] == "stalledDL":
|
||||
torrentInfo += " (Stalled)"
|
||||
|
||||
if not (downloadedRatio == 1 and torrent["last_activity"] < time.time()-7200):
|
||||
message.append(torrentInfo)
|
||||
|
||||
if downloadedRatio < 1 and torrent["state"] != "stalledDL":
|
||||
allDownloaded = False
|
||||
else:
|
||||
message.append("No torrents currently downloading")
|
||||
|
||||
if showMovies:
|
||||
message.append("")
|
||||
movieSectionTitle = "*Missing movies not downloading*"
|
||||
movieSectionTitleLine = "-"*((titleWidth-len(movieSectionTitle))//2)
|
||||
message.append(movieSectionTitleLine+movieSectionTitle+movieSectionTitleLine)
|
||||
movieList = requests.get(self.radarrURL+"movie?apiKey="+self.bot.credentials.radarrKey).json()
|
||||
movieQueue = requests.get(self.radarrURL+"queue?apiKey="+self.bot.credentials.radarrKey).json()
|
||||
movieQueueIDs = []
|
||||
|
||||
for queueItem in movieQueue["records"]:
|
||||
movieQueueIDs.append(queueItem["movieId"])
|
||||
|
||||
for movie in movieList:
|
||||
if not movie["hasFile"]:
|
||||
if movie["id"] not in movieQueueIDs:
|
||||
movieName = movie["title"]
|
||||
if len(movieName) > 40:
|
||||
if movieName[36] == " ":
|
||||
movieName = movieName[:36]+"...."
|
||||
else:
|
||||
movieName = movieName[:37]+"..."
|
||||
|
||||
while len(movieName) < 41:
|
||||
movieName += " "
|
||||
|
||||
if movie["monitored"]:
|
||||
movieInfo = movieName+"Could not find a torrent"
|
||||
else:
|
||||
movieInfo = movieName+"No torrent exists. Likely because the movie is not yet released on DVD"
|
||||
|
||||
message.append(movieInfo)
|
||||
|
||||
if showShows:
|
||||
message.append("")
|
||||
showSectionTitle = "*Missing shows not downloading*"
|
||||
showSectionTitleLine = "-"*((titleWidth-len(showSectionTitle))//2)
|
||||
message.append(showSectionTitleLine+showSectionTitle+showSectionTitleLine)
|
||||
|
||||
showList = requests.get(self.sonarrURL+"series?apiKey="+self.bot.credentials.sonarrKey).json()
|
||||
|
||||
for show in showList:
|
||||
if show["seasons"][0]["seasonNumber"] == 0:
|
||||
seasons = show["seasons"][1:]
|
||||
else:
|
||||
seasons = show["seasons"]
|
||||
if any(i["statistics"]["episodeCount"] != i["statistics"]["totalEpisodeCount"] for i in seasons):
|
||||
if all(i["statistics"]["episodeCount"] == 0 for i in seasons):
|
||||
message.append(show["title"] + " (all episodes)")
|
||||
else:
|
||||
if episodes:
|
||||
missingEpisodes = sum(i["statistics"]["totalEpisodeCount"] - i["statistics"]["episodeCount"] for i in seasons)
|
||||
message.append(show["title"] + f" ({missingEpisodes} episodes)")
|
||||
|
||||
message.append("-"*titleWidth)
|
||||
|
||||
messageText = "```"+"\n".join(message[1:])+"```"
|
||||
if messageText == "``````":
|
||||
messageText = "There are no torrents downloading right. If the torrent you're looking for was added more than 24 hours ago, it might already be on Bedre Netflix."
|
||||
return messageText, allDownloaded
|
||||
|
||||
async def downloading(self, ctx, content):
|
||||
async def SendLongMessage(ctx,messageText):
|
||||
if len(messageText) <= 1994:
|
||||
await ctx.send("```"+messageText+"```")
|
||||
else:
|
||||
cutOffIndex = messageText[:1994].rfind("\n")
|
||||
await ctx.send("```"+messageText[:cutOffIndex]+"```")
|
||||
await SendLongMessage(ctx,messageText[cutOffIndex+1:])
|
||||
|
||||
await self.bot.defer(ctx)
|
||||
|
||||
# showDM, showMovies, showShows, episodes
|
||||
params = [False, False, False, False]
|
||||
showDMArgs = ["d", "dm", "downloading", "downloadmanager"]
|
||||
showMoviesArgs = ["m", "movies"]
|
||||
showShowsArgs = ["s", "shows", "series"]
|
||||
episodesArgs = ["e", "episodes"]
|
||||
argList = [showDMArgs, showMoviesArgs, showShowsArgs, episodesArgs]
|
||||
inputArgs = []
|
||||
validArguments = True
|
||||
|
||||
while content != "" and validArguments:
|
||||
if content[0] == " ":
|
||||
content = content[1:]
|
||||
elif content[0] == "-":
|
||||
if content[1] == "-":
|
||||
argStart = 2
|
||||
if " " in content:
|
||||
argStop = content.find(" ")
|
||||
else:
|
||||
argStop = None
|
||||
else:
|
||||
argStart = 1
|
||||
argStop = 2
|
||||
|
||||
inputArgs.append(content[argStart:argStop])
|
||||
if argStop is None:
|
||||
content = ""
|
||||
else:
|
||||
content = content[argStop:]
|
||||
else:
|
||||
validArguments = False
|
||||
|
||||
if validArguments:
|
||||
for x, argAliases in enumerate(argList):
|
||||
argInInput = [i in inputArgs for i in argAliases]
|
||||
if any(argInInput):
|
||||
inputArgs.remove(argAliases[argInInput.index(True)])
|
||||
params[x] = True
|
||||
|
||||
if len(inputArgs) != 0 or (params[2] == False and params[3] == True):
|
||||
validArguments = False
|
||||
|
||||
showAnything = any(i for i in params)
|
||||
if validArguments and showAnything:
|
||||
messageText, allDownloaded = await self.genDownloadList(*params)
|
||||
if messageText.startswith("```"):
|
||||
|
||||
if len(messageText) <= 2000:
|
||||
if not allDownloaded:
|
||||
updatesLeft = 60
|
||||
messageText = messageText[:-3]+"\nThis message will update every 10 seconds for "+str(math.ceil(updatesLeft/6))+" more minutes\n```"
|
||||
oldMessage = await ctx.send(messageText)
|
||||
|
||||
while ((not allDownloaded) and updatesLeft > 0):
|
||||
await asyncio.sleep(10)
|
||||
updatesLeft -= 1
|
||||
messageText, allDownloaded = await self.genDownloadList(*params)
|
||||
messageText = messageText[:-3]+"\nThis message will update every 10 seconds for "+str(math.ceil(updatesLeft/6))+" more minutes\n```"
|
||||
await oldMessage.edit(content = messageText)
|
||||
|
||||
messageText, allDownloaded = await self.genDownloadList(*params)
|
||||
|
||||
if messageText.startswith("```"):
|
||||
if allDownloaded:
|
||||
self.bot.log("All torrents are downloaded")
|
||||
else:
|
||||
messageText = messageText[:-3]+"\nThis message will not update anymore\n```"
|
||||
self.bot.log("The message updated 20 times")
|
||||
|
||||
await oldMessage.edit(content = messageText)
|
||||
|
||||
else:
|
||||
await ctx.send(messageText)
|
||||
else:
|
||||
messageText = messageText[3:-3]
|
||||
await SendLongMessage(ctx,messageText)
|
||||
else:
|
||||
await ctx.send(messageText)
|
||||
else:
|
||||
await ctx.send("Invalid or repeated parameters. Use '/help downloading' to see valid parameters.")
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
"""The main module for Gwendolyn."""
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
__all__ = ["funcs", "utils", "Gwendolyn"]
|
||||
|
||||
from .gwendolyn_client import Gwendolyn
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Contains the EventCog, which runs code for specific bot events."""
|
||||
from discord.ext import commands # Has the cog class
|
||||
|
||||
|
||||
class EventCog(commands.Cog):
|
||||
"""Handles bot events."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the bot."""
|
||||
self.bot = bot
|
||||
self.bot.on_error = self.on_error
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
"""Log and set bot status when bot logs in."""
|
||||
await self.bot.event_handler.on_ready()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_slash_command(self, ctx):
|
||||
"""Log when a slash command is run."""
|
||||
await self.bot.event_handler.on_slash_command(ctx)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_slash_command_error(self, ctx, error):
|
||||
"""Log when a slash error occurs."""
|
||||
await self.bot.error_handler.on_slash_command_error(ctx, error)
|
||||
|
||||
async def on_error(self, method, *args, **kwargs): # pylint: disable=unused-argument
|
||||
"""Log when an error occurs."""
|
||||
await self.bot.error_handler.on_error(method)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_reaction_add(self, reaction, user):
|
||||
"""Handle when someone reacts to a message."""
|
||||
await self.bot.event_handler.on_reaction_add(reaction, user)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
"""Add the eventcog to the bot."""
|
||||
bot.add_cog(EventCog(bot))
|
||||
@@ -0,0 +1,177 @@
|
||||
"""Contains all the cogs that deal with game commands."""
|
||||
from discord.ext import commands # Has the cog class
|
||||
from discord_slash import cog_ext # Used for slash commands
|
||||
|
||||
from gwendolyn.utils import get_params # pylint: disable=import-error
|
||||
|
||||
params = get_params()
|
||||
|
||||
|
||||
class GamesCog(commands.Cog):
|
||||
"""Contains miscellaneous game commands."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the cog."""
|
||||
self.bot = bot
|
||||
|
||||
@cog_ext.cog_slash(**params["balance"])
|
||||
async def balance(self, ctx):
|
||||
"""Check user balance."""
|
||||
await self.bot.money.sendBalance(ctx)
|
||||
|
||||
@cog_ext.cog_slash(**params["give"])
|
||||
async def give(self, ctx, user, amount):
|
||||
"""Give another user an amount of GwendoBucks."""
|
||||
await self.bot.money.giveMoney(ctx, user, amount)
|
||||
|
||||
@cog_ext.cog_slash(**params["invest"])
|
||||
async def invest(self, ctx, parameters="check"):
|
||||
"""Invest GwendoBucks in the stock market."""
|
||||
await self.bot.games.invest.parseInvest(ctx, parameters)
|
||||
|
||||
@cog_ext.cog_slash(**params["trivia"])
|
||||
async def trivia(self, ctx, answer=""):
|
||||
"""Run a game of trivia."""
|
||||
await self.bot.games.trivia.triviaParse(ctx, answer)
|
||||
|
||||
|
||||
class BlackjackCog(commands.Cog):
|
||||
"""Contains the blackjack commands."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the cog."""
|
||||
self.bot = bot
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_start"])
|
||||
async def blackjack_start(self, ctx):
|
||||
"""Start a game of blackjack."""
|
||||
await self.bot.games.blackjack.start(ctx)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_bet"])
|
||||
async def blackjack_bet(self, ctx, bet):
|
||||
"""Enter the game of blackjack with a bet."""
|
||||
await self.bot.games.blackjack.enter_game(ctx, bet)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_stand"])
|
||||
async def blackjack_stand(self, ctx, hand=""):
|
||||
"""Stand on your hand in blackjack."""
|
||||
await self.bot.games.blackjack.stand(ctx, hand)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_hit"])
|
||||
async def blackjack_hit(self, ctx, hand=0):
|
||||
"""Hit on your hand in blackjack."""
|
||||
await self.bot.games.blackjack.hit(ctx, hand)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_double"])
|
||||
async def blackjack_double(self, ctx, hand=0):
|
||||
"""Double in blackjack."""
|
||||
await self.bot.games.blackjack.double(ctx, hand)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_split"])
|
||||
async def blackjack_split(self, ctx, hand=0):
|
||||
"""Split your hand in blackjack."""
|
||||
await self.bot.games.blackjack.split(ctx, hand)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_hilo"])
|
||||
async def blackjack_hilo(self, ctx):
|
||||
"""Get the hilo value for the deck in blackjack."""
|
||||
await self.bot.games.blackjack.hilo(ctx)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_shuffle"])
|
||||
async def blackjack_shuffle(self, ctx):
|
||||
"""Shuffle the blackjack game."""
|
||||
await self.bot.games.blackjack.shuffle(ctx)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["blackjack_cards"])
|
||||
async def blackjack_cards(self, ctx):
|
||||
"""Get the amount of cards left in the blackjack deck."""
|
||||
await self.bot.games.blackjack.cards(ctx)
|
||||
|
||||
|
||||
class ConnectFourCog(commands.Cog):
|
||||
"""Contains all the connect four commands."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the cog."""
|
||||
self.bot = bot
|
||||
|
||||
@cog_ext.cog_subcommand(**params["connect_four_start_user"])
|
||||
async def connect_four_start_user(self, ctx, user):
|
||||
"""Start a game of connect four against another user."""
|
||||
await self.bot.games.connect_four.start(ctx, user)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["connect_four_start_gwendolyn"])
|
||||
async def connect_four_start_gwendolyn(self, ctx, difficulty=3):
|
||||
"""Start a game of connect four against Gwendolyn."""
|
||||
await self.bot.games.connect_four.start(ctx, difficulty)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["connect_four_surrender"])
|
||||
async def connect_four_surrender(self, ctx):
|
||||
"""Surrender the game of connect four."""
|
||||
await self.bot.games.connect_four.surrender(ctx)
|
||||
|
||||
|
||||
class HangmanCog(commands.Cog):
|
||||
"""Contains all the hangman commands."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the cog."""
|
||||
self.bot = bot
|
||||
|
||||
@cog_ext.cog_subcommand(**params["hangman_start"])
|
||||
async def hangman_start(self, ctx):
|
||||
"""Start a game of hangman."""
|
||||
await self.bot.games.hangman.start(ctx)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["hangman_stop"])
|
||||
async def hangman_stop(self, ctx):
|
||||
"""Stop the current game of hangman."""
|
||||
await self.bot.games.hangman.stop(ctx)
|
||||
|
||||
|
||||
class HexCog(commands.Cog):
|
||||
"""Contains all the hex commands."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the cog."""
|
||||
self.bot = bot
|
||||
self.hex = self.bot.games.hex
|
||||
|
||||
@cog_ext.cog_subcommand(**params["hex_start_user"])
|
||||
async def hex_start_user(self, ctx, user):
|
||||
"""Start a game of hex against another player."""
|
||||
await self.hex.start(ctx, user)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["hex_start_gwendolyn"])
|
||||
async def hex_start_gwendolyn(self, ctx, difficulty=2):
|
||||
"""Start a game of hex against Gwendolyn."""
|
||||
await self.hex.start(ctx, difficulty)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["hex_place"])
|
||||
async def hex_place(self, ctx, coordinates):
|
||||
"""Place a piece in the hex game."""
|
||||
await self.hex.placeHex(ctx, coordinates, f"#{ctx.author.id}")
|
||||
|
||||
@cog_ext.cog_subcommand(**params["hex_undo"])
|
||||
async def hex_undo(self, ctx):
|
||||
"""Undo your last hex move."""
|
||||
await self.hex.undo(ctx)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["hex_swap"])
|
||||
async def hex_swap(self, ctx):
|
||||
"""Perform a hex swap."""
|
||||
await self.hex.swap(ctx)
|
||||
|
||||
@cog_ext.cog_subcommand(**params["hex_surrender"])
|
||||
async def hex_surrender(self, ctx):
|
||||
"""Surrender the hex game."""
|
||||
await self.hex.surrender(ctx)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
"""Add all the cogs to the bot."""
|
||||
bot.add_cog(GamesCog(bot))
|
||||
bot.add_cog(BlackjackCog(bot))
|
||||
bot.add_cog(ConnectFourCog(bot))
|
||||
bot.add_cog(HangmanCog(bot))
|
||||
bot.add_cog(HexCog(bot))
|
||||
@@ -0,0 +1,32 @@
|
||||
"""Contains the LookupCog, which deals with the lookup commands."""
|
||||
from discord.ext import commands # Has the cog class
|
||||
from discord_slash import cog_ext # Used for slash commands
|
||||
|
||||
from gwendolyn.utils import get_params # pylint: disable=import-error
|
||||
|
||||
params = get_params()
|
||||
|
||||
|
||||
class LookupCog(commands.Cog):
|
||||
"""Contains the lookup commands."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the cog."""
|
||||
self.bot = bot
|
||||
|
||||
# Looks up a spell
|
||||
@cog_ext.cog_slash(**params["spell"])
|
||||
async def spell(self, ctx, query):
|
||||
"""Look up a spell."""
|
||||
await self.bot.lookup_funcs.spellFunc(ctx, query)
|
||||
|
||||
# Looks up a monster
|
||||
@cog_ext.cog_slash(**params["monster"])
|
||||
async def monster(self, ctx, query):
|
||||
"""Look up a monster."""
|
||||
await self.bot.lookup_funcs.monsterFunc(ctx, query)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
"""Add the cog to the bot."""
|
||||
bot.add_cog(LookupCog(bot))
|
||||
@@ -0,0 +1,99 @@
|
||||
"""Contains the MiscCog, which deals with miscellaneous commands."""
|
||||
from discord.ext import commands # Has the cog class
|
||||
from discord_slash import cog_ext # Used for slash commands
|
||||
|
||||
from gwendolyn.utils import get_params # pylint: disable=import-error
|
||||
|
||||
params = get_params()
|
||||
|
||||
|
||||
class MiscCog(commands.Cog):
|
||||
"""Contains the miscellaneous commands."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the cog."""
|
||||
self.bot = bot
|
||||
self.bot.remove_command("help")
|
||||
self.generators = bot.other.generators
|
||||
self.plex = bot.other.plex
|
||||
self.nerd_shit = bot.other.nerd_shit
|
||||
|
||||
@cog_ext.cog_slash(**params["ping"])
|
||||
async def ping(self, ctx):
|
||||
"""Send the bot's latency."""
|
||||
await ctx.send(f"Pong!\nLatency is {round(self.bot.latency * 1000)}ms")
|
||||
|
||||
@cog_ext.cog_slash(**params["stop"])
|
||||
async def stop(self, ctx):
|
||||
"""Stop the bot."""
|
||||
await self.bot.stop(ctx)
|
||||
|
||||
@cog_ext.cog_slash(**params["help"])
|
||||
async def help_command(self, ctx, command=""):
|
||||
"""Get help for commands."""
|
||||
await self.bot.other.helpFunc(ctx, command)
|
||||
|
||||
@cog_ext.cog_slash(**params["thank"])
|
||||
async def thank(self, ctx):
|
||||
"""Thank the bot."""
|
||||
await ctx.send("You're welcome :blush:")
|
||||
|
||||
@cog_ext.cog_slash(**params["hello"])
|
||||
async def hello(self, ctx):
|
||||
"""Greet the bot."""
|
||||
await self.bot.other.helloFunc(ctx)
|
||||
|
||||
@cog_ext.cog_slash(**params["roll"])
|
||||
async def roll(self, ctx, dice="1d20"):
|
||||
"""Roll dice."""
|
||||
await self.bot.other.rollDice(ctx, dice)
|
||||
|
||||
@cog_ext.cog_slash(**params["image"])
|
||||
async def image(self, ctx):
|
||||
"""Get a random image from Bing."""
|
||||
await self.bot.other.imageFunc(ctx)
|
||||
|
||||
@cog_ext.cog_slash(**params["movie"])
|
||||
async def movie(self, ctx):
|
||||
"""Get a random movie from the Plex server."""
|
||||
await self.bot.other.movieFunc(ctx)
|
||||
|
||||
@cog_ext.cog_slash(**params["name"])
|
||||
async def name(self, ctx):
|
||||
"""Generate a random name."""
|
||||
await self.generators.nameGen(ctx)
|
||||
|
||||
@cog_ext.cog_slash(**params["tavern"])
|
||||
async def tavern(self, ctx):
|
||||
"""Generate a random tavern name."""
|
||||
await self.generators.tavernGen(ctx)
|
||||
|
||||
@cog_ext.cog_slash(**params["wiki"])
|
||||
async def wiki(self, ctx, wiki_page=""):
|
||||
"""Get a page on a fandom wiki."""
|
||||
await self.bot.other.findWikiPage(ctx, wiki_page)
|
||||
|
||||
@cog_ext.cog_slash(**params["add_movie"])
|
||||
async def add_movie(self, ctx, movie):
|
||||
"""Search for a movie and add it to the Plex server."""
|
||||
await self.plex.request_movie(ctx, movie)
|
||||
|
||||
@cog_ext.cog_slash(**params["add_show"])
|
||||
async def add_show(self, ctx, show):
|
||||
"""Search for a show and add it to the Plex server."""
|
||||
await self.plex.request_show(ctx, show)
|
||||
|
||||
@cog_ext.cog_slash(**params["downloading"])
|
||||
async def downloading(self, ctx, parameters="-d"):
|
||||
"""Get the current downloading torrents."""
|
||||
await self.plex.downloading(ctx, parameters)
|
||||
|
||||
@cog_ext.cog_slash(**params["wolf"])
|
||||
async def wolf(self, ctx, query):
|
||||
"""Perform a search on Wolfram Alpha."""
|
||||
await self.nerd_shit.wolfSearch(ctx, query)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
"""Add the cog to the bot."""
|
||||
bot.add_cog(MiscCog(bot))
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Contains the StarWarsCog, which deals with Star Wars commands."""
|
||||
from discord.ext import commands
|
||||
from discord_slash import cog_ext
|
||||
|
||||
from gwendolyn.utils import get_params # pylint: disable=import-error
|
||||
|
||||
params = get_params()
|
||||
|
||||
|
||||
class StarWarsCog(commands.Cog):
|
||||
"""Contains the Star Wars commands."""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the cog."""
|
||||
self.bot = bot
|
||||
|
||||
@cog_ext.cog_slash(**params["star_wars_roll"])
|
||||
async def star_wars_roll(self, ctx, dice=""):
|
||||
"""Roll Star Wars dice."""
|
||||
await self.bot.star_wars.roll.parseRoll(ctx, dice)
|
||||
|
||||
@cog_ext.cog_slash(**params["star_wars_destiny"])
|
||||
async def star_wars_destiny(self, ctx, parameters=""):
|
||||
"""Control Star Wars destiny points."""
|
||||
await self.bot.star_wars.destiny.parseDestiny(ctx, parameters)
|
||||
|
||||
@cog_ext.cog_slash(**params["star_wars_crit"])
|
||||
async def star_wars_crit(self, ctx, severity: int = 0):
|
||||
"""Roll for critical injuries."""
|
||||
await self.bot.star_wars.roll.critRoll(ctx, severity)
|
||||
|
||||
@cog_ext.cog_slash(**params["star_wars_character"])
|
||||
async def star_wars_character(self, ctx, parameters=""):
|
||||
"""Access and change Star Wars character sheet data."""
|
||||
await self.bot.star_wars.character.parseChar(ctx, parameters)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
"""Add the cog to the bot."""
|
||||
bot.add_cog(StarWarsCog(bot))
|
||||
@@ -1,6 +1,6 @@
|
||||
"""A collection of all Gwendolyn functions."""
|
||||
|
||||
__all__ = ["Games" , "Money", "LookupFuncs", "StarWars"]
|
||||
__all__ = ["Games", "Money", "LookupFuncs", "StarWars"]
|
||||
|
||||
from .games import Money, Games
|
||||
|
||||
@@ -8,4 +8,4 @@ from .lookup import LookupFuncs
|
||||
|
||||
from .other import Other
|
||||
|
||||
from .starWarsFuncs import StarWars
|
||||
from .star_wars_funcs import StarWars
|
||||
@@ -3,4 +3,4 @@
|
||||
__all__ = ["Money", "Games"]
|
||||
|
||||
from .money import Money
|
||||
from .gamesContainer import Games
|
||||
from .games_container import Games
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,48 @@
|
||||
"""
|
||||
Has a container for game functions.
|
||||
|
||||
*Classes*
|
||||
---------
|
||||
Games
|
||||
Container for game functions.
|
||||
"""
|
||||
|
||||
|
||||
from .invest import Invest
|
||||
from .trivia import Trivia
|
||||
from .blackjack import Blackjack
|
||||
from .connect_four import ConnectFour
|
||||
from .hangman import Hangman
|
||||
from .hex import HexGame
|
||||
|
||||
|
||||
class Games():
|
||||
"""
|
||||
Contains game classes.
|
||||
|
||||
*Attributes*
|
||||
------------
|
||||
bot: Gwendolyn
|
||||
The instance of Gwendolyn.
|
||||
invest
|
||||
Contains investment functions.
|
||||
blackjack
|
||||
Contains blackjack functions.
|
||||
connect_four
|
||||
Contains connect four functions.
|
||||
hangman
|
||||
Contains hangman functions.
|
||||
hex
|
||||
Contains hex functions
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the container."""
|
||||
self.bot = bot
|
||||
|
||||
self.invest = Invest(bot)
|
||||
self.trivia = Trivia(bot)
|
||||
self.blackjack = Blackjack(bot)
|
||||
self.connect_four = ConnectFour(bot)
|
||||
self.hangman = Hangman(bot)
|
||||
self.hex = HexGame(bot)
|
||||
@@ -0,0 +1,627 @@
|
||||
"""
|
||||
Deals with commands and logic for hangman games.
|
||||
|
||||
*Classes*
|
||||
---------
|
||||
Hangman()
|
||||
Deals with the game logic of hangman.
|
||||
DrawHangman()
|
||||
Draws the image shown to the player.
|
||||
"""
|
||||
import datetime # Used for generating the game id
|
||||
import string # string.ascii_uppercase used
|
||||
import math # Used by DrawHangman(), mainly for drawing circles
|
||||
import random # Used to draw poorly
|
||||
import requests # Used for getting the word in Hangman.start()
|
||||
import discord # Used for discord.file and type hints
|
||||
|
||||
from discord_slash.context import SlashContext # Used for typehints
|
||||
from PIL import ImageDraw, Image, ImageFont # Used to draw the image
|
||||
|
||||
|
||||
class Hangman():
|
||||
"""
|
||||
Controls hangman commands and game logic.
|
||||
|
||||
*Methods*
|
||||
---------
|
||||
start(ctx: SlashContext)
|
||||
stop(ctx: SlashContext)
|
||||
guess(message: discord.message, user: str, guess: str)
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""
|
||||
Initialize the class.
|
||||
|
||||
*Attributes*
|
||||
------------
|
||||
draw: DrawHangman
|
||||
The DrawHangman used to draw the hangman image.
|
||||
API_url: str
|
||||
The url to get the words from.
|
||||
APIPARAMS: dict
|
||||
The parameters to pass to every api call.
|
||||
"""
|
||||
self.__bot = bot
|
||||
self.__draw = DrawHangman(bot)
|
||||
self.__API_url = "https://api.wordnik.com/v4/words.json/randomWords?" # pylint: disable=invalid-name
|
||||
api_key = self.__bot.credentials["wordnik_key"]
|
||||
self.__APIPARAMS = { # pylint: disable=invalid-name
|
||||
"hasDictionaryDef": True,
|
||||
"minCorpusCount": 5000,
|
||||
"maxCorpusCount": -1,
|
||||
"minDictionaryCount": 1,
|
||||
"maxDictionaryCount": -1,
|
||||
"minLength": 3,
|
||||
"maxLength": 11,
|
||||
"limit": 1,
|
||||
"api_key": api_key
|
||||
}
|
||||
|
||||
async def start(self, ctx: SlashContext):
|
||||
"""
|
||||
Start a game of hangman.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
ctx: SlashContext
|
||||
The context of the command.
|
||||
"""
|
||||
await self.__bot.defer(ctx)
|
||||
channel = str(ctx.channel_id)
|
||||
user = f"#{ctx.author.id}"
|
||||
game = self.__bot.database["hangman games"].find_one({"_id": channel})
|
||||
user_name = self.__bot.database_funcs.get_name(user)
|
||||
started_game = False
|
||||
|
||||
if game is None:
|
||||
word = "-"
|
||||
while "-" in word or "." in word:
|
||||
response = requests.get(self.__API_url, params=self.__APIPARAMS)
|
||||
word = list(response.json()[0]["word"].upper())
|
||||
|
||||
self.__bot.log("Found the word \""+"".join(word)+"\"")
|
||||
guessed = [False] * len(word)
|
||||
game_id = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
new_game = {
|
||||
"_id": channel,
|
||||
"player": user,
|
||||
"guessed letters": [],
|
||||
"word": word,
|
||||
"game ID": game_id,
|
||||
"misses": 0,
|
||||
"guessed": guessed
|
||||
}
|
||||
self.__bot.database["hangman games"].insert_one(new_game)
|
||||
|
||||
remaining_letters = list(string.ascii_uppercase)
|
||||
|
||||
self.__draw.draw_image(channel)
|
||||
|
||||
log_message = "Game started"
|
||||
send_message = f"{user_name} started game of hangman."
|
||||
started_game = True
|
||||
else:
|
||||
log_message = "There was already a game going on"
|
||||
send_message = self.__bot.long_strings["Hangman going on"]
|
||||
|
||||
self.__bot.log(log_message)
|
||||
await ctx.send(send_message)
|
||||
|
||||
if started_game:
|
||||
boards_path = "gwendolyn/resources/games/hangman_boards/"
|
||||
file_path = f"{boards_path}hangman_board{channel}.png"
|
||||
new_image = await ctx.channel.send(file=discord.File(file_path))
|
||||
|
||||
blank_message = await ctx.channel.send("_ _")
|
||||
reaction_messages = {
|
||||
new_image: remaining_letters[:15],
|
||||
blank_message: remaining_letters[15:]
|
||||
}
|
||||
|
||||
old_messages = f"{new_image.id}\n{blank_message.id}"
|
||||
old_images_path = "gwendolyn/resources/games/old_images/"
|
||||
|
||||
with open(old_images_path+f"hangman{channel}", "w") as file_pointer:
|
||||
file_pointer.write(old_messages)
|
||||
|
||||
for message, letters in reaction_messages.items():
|
||||
for letter in letters:
|
||||
emoji = chr(ord(letter)+127397)
|
||||
await message.add_reaction(emoji)
|
||||
|
||||
async def stop(self, ctx: SlashContext):
|
||||
"""
|
||||
Stop the game of hangman.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
ctx: SlashContext
|
||||
The context of the command.
|
||||
"""
|
||||
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})
|
||||
old_images_path = "gwendolyn/resources/games/old_images/"
|
||||
|
||||
with open(old_images_path+f"hangman{channel}", "r") as file_pointer:
|
||||
messages = file_pointer.read().splitlines()
|
||||
|
||||
for message in messages:
|
||||
old_message = await ctx.channel.fetch_message(int(message))
|
||||
self.__bot.log("Deleting old message")
|
||||
await old_message.delete()
|
||||
|
||||
await ctx.send("Game stopped")
|
||||
|
||||
async def guess(self, message: discord.Message, user: str, guess: str):
|
||||
"""
|
||||
Guess a letter.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
message: discord.Message
|
||||
The message that the reaction was placed on.
|
||||
user: str
|
||||
The id of the user.
|
||||
guess: str
|
||||
The guess.
|
||||
"""
|
||||
channel = str(message.channel.id)
|
||||
hangman_games = self.__bot.database["hangman games"]
|
||||
game = hangman_games.find_one({"_id": channel})
|
||||
|
||||
game_exists = (game is not None)
|
||||
single_letter = (len(guess) == 1 and guess.isalpha())
|
||||
new_guess = (guess not in game["guessed letters"])
|
||||
valid_guess = (game_exists and single_letter and new_guess)
|
||||
|
||||
if valid_guess:
|
||||
self.__bot.log("Guessed the letter")
|
||||
correct_guess = 0
|
||||
|
||||
for i, letter in enumerate(game["word"]):
|
||||
if guess == letter:
|
||||
correct_guess += 1
|
||||
updater = {"$set": {f"guessed.{i}": True}}
|
||||
hangman_games.update_one({"_id": channel}, updater)
|
||||
|
||||
if correct_guess == 0:
|
||||
updater = {"$inc": {"misses": 1}}
|
||||
hangman_games.update_one({"_id": channel}, updater)
|
||||
|
||||
updater = {"$push": {"guessed letters": guess}}
|
||||
hangman_games.update_one({"_id": channel}, updater)
|
||||
|
||||
remaining_letters = list(string.ascii_uppercase)
|
||||
|
||||
game = hangman_games.find_one({"_id": channel})
|
||||
|
||||
for letter in game["guessed letters"]:
|
||||
remaining_letters.remove(letter)
|
||||
|
||||
if correct_guess == 1:
|
||||
send_message = "Guessed {}. There was 1 {} in the word."
|
||||
send_message = send_message.format(guess, guess)
|
||||
else:
|
||||
send_message = "Guessed {}. There were {} {}s in the word."
|
||||
send_message = send_message.format(guess, correct_guess, guess)
|
||||
|
||||
self.__draw.draw_image(channel)
|
||||
|
||||
if game["misses"] == 6:
|
||||
hangman_games.delete_one({"_id": channel})
|
||||
send_message += self.__bot.long_strings["Hangman lost game"]
|
||||
remaining_letters = []
|
||||
elif all(game["guessed"]):
|
||||
hangman_games.delete_one({"_id": channel})
|
||||
self.__bot.money.addMoney(user, 15)
|
||||
send_message += self.__bot.long_strings["Hangman guessed word"]
|
||||
remaining_letters = []
|
||||
|
||||
await message.channel.send(send_message)
|
||||
old_images_path = "gwendolyn/resources/games/old_images/"
|
||||
|
||||
with open(old_images_path+f"hangman{channel}", "r") as file_pointer:
|
||||
old_message_ids = file_pointer.read().splitlines()
|
||||
|
||||
for old_id in old_message_ids:
|
||||
old_message = await message.channel.fetch_message(int(old_id))
|
||||
self.__bot.log("Deleting old message")
|
||||
await old_message.delete()
|
||||
|
||||
boards_path = "gwendolyn/resources/games/hangman_boards/"
|
||||
file_path = f"{boards_path}hangman_board{channel}.png"
|
||||
new_image = await message.channel.send(file=discord.File(file_path))
|
||||
|
||||
if len(remaining_letters) > 0:
|
||||
if len(remaining_letters) > 15:
|
||||
blank_message = await message.channel.send("_ _")
|
||||
reaction_messages = {
|
||||
new_image: remaining_letters[:15],
|
||||
blank_message: remaining_letters[15:]
|
||||
}
|
||||
else:
|
||||
blank_message = ""
|
||||
reaction_messages = {new_image: remaining_letters}
|
||||
|
||||
if blank_message != "":
|
||||
old_messages = f"{new_image.id}\n{blank_message.id}"
|
||||
else:
|
||||
old_messages = str(new_image.id)
|
||||
|
||||
old_images_path = "gwendolyn/resources/games/old_images/"
|
||||
old_image_path = old_images_path + f"hangman{channel}"
|
||||
with open(old_image_path, "w") as file_pointer:
|
||||
file_pointer.write(old_messages)
|
||||
|
||||
for message, letters in reaction_messages.items():
|
||||
for letter in letters:
|
||||
emoji = chr(ord(letter)+127397)
|
||||
await message.add_reaction(emoji)
|
||||
|
||||
|
||||
class DrawHangman():
|
||||
"""
|
||||
Draws the image of the hangman game.
|
||||
|
||||
*Methods*
|
||||
---------
|
||||
draw_image(channel: str)
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""
|
||||
Initialize the class.
|
||||
|
||||
*Attributes*
|
||||
------------
|
||||
CIRCLESIZE
|
||||
LINEWIDTH
|
||||
BODYSIZE
|
||||
LIMBSIZE
|
||||
ARMPOSITION
|
||||
MANX, MANY
|
||||
LETTERLINELENGTH
|
||||
LETTERLINEDISTANCE
|
||||
GALLOWX, GALLOWY
|
||||
PHI
|
||||
FONT
|
||||
SMALLFONT
|
||||
"""
|
||||
self.__bot = bot
|
||||
# pylint: disable=invalid-name
|
||||
self.__CIRCLESIZE = 120
|
||||
self.__LINEWIDTH = 12
|
||||
|
||||
self.__BODYSIZE = 210
|
||||
self.__LIMBSIZE = 60
|
||||
self.__ARMPOSITION = 60
|
||||
|
||||
self.__MANX = (self.__LIMBSIZE*2)
|
||||
self.__MANY = (self.__CIRCLESIZE+self.__BODYSIZE+self.__LIMBSIZE)
|
||||
MANPADDING = self.__LINEWIDTH*4
|
||||
self.__MANX += MANPADDING
|
||||
self.__MANY += MANPADDING
|
||||
|
||||
self.__LETTERLINELENGTH = 90
|
||||
self.__LETTERLINEDISTANCE = 30
|
||||
|
||||
self.__GALLOWX, self.__GALLOWY = 360, 600
|
||||
self.__PHI = 1-(1 / ((1 + 5 ** 0.5) / 2))
|
||||
|
||||
LETTERSIZE = 75 # Wrong guesses letter size
|
||||
WORDSIZE = 70 # Correct guesses letter size
|
||||
|
||||
FONTPATH = "gwendolyn/resources/fonts/comic-sans-bold.ttf"
|
||||
self.__FONT = ImageFont.truetype(FONTPATH, LETTERSIZE)
|
||||
self.__SMALLFONT = ImageFont.truetype(FONTPATH, WORDSIZE)
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
def __deviate(self, pre_deviance: int, pre_deviance_accuracy: int,
|
||||
position_change: float, maxmin: int,
|
||||
max_acceleration: float):
|
||||
random_deviance = random.uniform(-position_change, position_change)
|
||||
deviance_accuracy = pre_deviance_accuracy + random_deviance
|
||||
if deviance_accuracy > maxmin * max_acceleration:
|
||||
deviance_accuracy = maxmin * max_acceleration
|
||||
elif deviance_accuracy < -maxmin * max_acceleration:
|
||||
deviance_accuracy = -maxmin * max_acceleration
|
||||
|
||||
deviance = pre_deviance + deviance_accuracy
|
||||
if deviance > maxmin:
|
||||
deviance = maxmin
|
||||
elif deviance < -maxmin:
|
||||
deviance = -maxmin
|
||||
return deviance, deviance_accuracy
|
||||
|
||||
def __bad_circle(self):
|
||||
circle_padding = (self.__LINEWIDTH*3)
|
||||
image_width = self.__CIRCLESIZE+circle_padding
|
||||
image_size = (image_width, image_width)
|
||||
background = Image.new("RGBA", image_size, color=(0, 0, 0, 0))
|
||||
|
||||
drawer = ImageDraw.Draw(background, "RGBA")
|
||||
middle = (self.__CIRCLESIZE+(self.__LINEWIDTH*3))/2
|
||||
deviance_x = 0
|
||||
deviance_y = 0
|
||||
deviance_accuracy_x = 0
|
||||
deviance_accuracy_y = 0
|
||||
start = random.randint(-100, -80)
|
||||
degreess_amount = 360 + random.randint(-10, 30)
|
||||
|
||||
for degree in range(degreess_amount):
|
||||
deviance_x, deviance_accuracy_x = self.__deviate(
|
||||
deviance_x,
|
||||
deviance_accuracy_x,
|
||||
self.__LINEWIDTH/100,
|
||||
self.__LINEWIDTH,
|
||||
0.03
|
||||
)
|
||||
deviance_y, deviance_accuracy_y = self.__deviate(
|
||||
deviance_y,
|
||||
deviance_accuracy_y,
|
||||
self.__LINEWIDTH/100,
|
||||
self.__LINEWIDTH,
|
||||
0.03
|
||||
)
|
||||
|
||||
radians = math.radians(degree+start)
|
||||
circle_x = (math.cos(radians) * (self.__CIRCLESIZE/2))
|
||||
circle_y = (math.sin(radians) * (self.__CIRCLESIZE/2))
|
||||
|
||||
position_x = middle + circle_x - (self.__LINEWIDTH/2) + deviance_x
|
||||
position_y = middle + circle_y - (self.__LINEWIDTH/2) + deviance_y
|
||||
|
||||
circle_position = [
|
||||
(position_x, position_y),
|
||||
(position_x+self.__LINEWIDTH, position_y+self.__LINEWIDTH)
|
||||
]
|
||||
drawer.ellipse(circle_position, fill=(0, 0, 0, 255))
|
||||
|
||||
return background
|
||||
|
||||
def __bad_line(self, length: int, rotated: bool = False):
|
||||
if rotated:
|
||||
width, height = length+self.__LINEWIDTH*3, self.__LINEWIDTH*3
|
||||
else:
|
||||
width, height = self.__LINEWIDTH*3, length+self.__LINEWIDTH*3
|
||||
background = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
|
||||
|
||||
drawer = ImageDraw.Draw(background, "RGBA")
|
||||
|
||||
possible_deviance = int(self.__LINEWIDTH/3)
|
||||
deviance_x = random.randint(-possible_deviance, possible_deviance)
|
||||
deviance_y = 0
|
||||
deviance_accuracy_x = 0
|
||||
deviance_accuracy_y = 0
|
||||
|
||||
for pixel in range(length):
|
||||
deviance_x, deviance_accuracy_x = self.__deviate(
|
||||
deviance_x,
|
||||
deviance_accuracy_x,
|
||||
self.__LINEWIDTH/1000,
|
||||
self.__LINEWIDTH,
|
||||
0.004
|
||||
)
|
||||
deviance_y, deviance_accuracy_y = self.__deviate(
|
||||
deviance_y,
|
||||
deviance_accuracy_y,
|
||||
self.__LINEWIDTH/1000,
|
||||
self.__LINEWIDTH,
|
||||
0.004
|
||||
)
|
||||
|
||||
if rotated:
|
||||
position_x = self.__LINEWIDTH + pixel + deviance_x
|
||||
position_y = self.__LINEWIDTH + deviance_y
|
||||
else:
|
||||
position_x = self.__LINEWIDTH + deviance_x
|
||||
position_y = self.__LINEWIDTH + pixel + deviance_y
|
||||
|
||||
circle_position = [
|
||||
(position_x, position_y),
|
||||
(position_x+self.__LINEWIDTH, position_y+self.__LINEWIDTH)
|
||||
]
|
||||
drawer.ellipse(circle_position, fill=(0, 0, 0, 255))
|
||||
|
||||
return background
|
||||
|
||||
def __draw_man(self, misses: int, seed: str):
|
||||
random.seed(seed)
|
||||
man_size = (self.__MANX, self.__MANY)
|
||||
background = Image.new("RGBA", man_size, color=(0, 0, 0, 0))
|
||||
|
||||
if misses >= 1:
|
||||
head = self.__bad_circle()
|
||||
paste_x = (self.__MANX-(self.__CIRCLESIZE+(self.__LINEWIDTH*3)))//2
|
||||
paste_position = (paste_x, 0)
|
||||
background.paste(head, paste_position, head)
|
||||
if misses >= 2:
|
||||
body = self.__bad_line(self.__BODYSIZE)
|
||||
paste_x = (self.__MANX-(self.__LINEWIDTH*3))//2
|
||||
paste_position = (paste_x, self.__CIRCLESIZE)
|
||||
background.paste(body, paste_position, body)
|
||||
|
||||
if misses >= 3:
|
||||
limbs = random.sample(["rl", "ll", "ra", "la"], min(misses-2, 4))
|
||||
else:
|
||||
limbs = []
|
||||
|
||||
random.seed(seed)
|
||||
|
||||
for limb in limbs:
|
||||
limb_drawing = self.__bad_line(self.__LIMBSIZE, True)
|
||||
x_position = (self.__MANX-(self.__LINEWIDTH*3))//2
|
||||
|
||||
if limb[1] == "a":
|
||||
rotation = random.randint(-45, 45)
|
||||
shift = math.sin(math.radians(rotation))
|
||||
line_length = self.__LIMBSIZE+(self.__LINEWIDTH*3)
|
||||
compensation = int(shift*line_length)
|
||||
limb_drawing = limb_drawing.rotate(rotation, expand=1)
|
||||
y_position = self.__CIRCLESIZE + self.__ARMPOSITION
|
||||
if limb == "ra":
|
||||
compensation = min(-compensation, 0)
|
||||
else:
|
||||
x_position -= self.__LIMBSIZE
|
||||
compensation = min(compensation, 0)
|
||||
|
||||
y_position += compensation
|
||||
else:
|
||||
rotation = random.randint(-15, 15)
|
||||
y_position = self.__CIRCLESIZE+self.__BODYSIZE-self.__LINEWIDTH
|
||||
if limb == "rl":
|
||||
limb_drawing = limb_drawing.rotate(rotation-45, expand=1)
|
||||
else:
|
||||
x_position += -limb_drawing.size[0]+self.__LINEWIDTH*3
|
||||
limb_drawing = limb_drawing.rotate(rotation+45, expand=1)
|
||||
|
||||
paste_position = (x_position, y_position)
|
||||
background.paste(limb_drawing, paste_position, limb_drawing)
|
||||
|
||||
return background
|
||||
|
||||
def __bad_text(self, text: str, big: bool, color: tuple = (0, 0, 0, 255)):
|
||||
if big:
|
||||
font = self.__FONT
|
||||
else:
|
||||
font = self.__SMALLFONT
|
||||
width, height = font.getsize(text)
|
||||
img = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
|
||||
drawer = ImageDraw.Draw(img, "RGBA")
|
||||
|
||||
drawer.text((0, 0), text, font=font, fill=color)
|
||||
return img
|
||||
|
||||
def __draw_gallows(self):
|
||||
gallow_size = (self.__GALLOWX, self.__GALLOWY)
|
||||
background = Image.new("RGBA", gallow_size, color=(0, 0, 0, 0))
|
||||
|
||||
bottom_line = self.__bad_line(int(self.__GALLOWX * 0.75), True)
|
||||
bottom_line_x = int(self.__GALLOWX * 0.125)
|
||||
bottom_line_y = self.__GALLOWY-(self.__LINEWIDTH*4)
|
||||
paste_position = (bottom_line_x, bottom_line_y)
|
||||
background.paste(bottom_line, paste_position, bottom_line)
|
||||
|
||||
line_two = self.__bad_line(self.__GALLOWY-self.__LINEWIDTH*6)
|
||||
line_two_x = int(self.__GALLOWX*(0.75*self.__PHI))
|
||||
line_two_y = self.__LINEWIDTH*2
|
||||
paste_position = (line_two_x, line_two_y)
|
||||
background.paste(line_two, paste_position, line_two)
|
||||
|
||||
top_line = self.__bad_line(int(self.__GALLOWY*0.30), True)
|
||||
paste_x = int(self.__GALLOWX*(0.75*self.__PHI))-self.__LINEWIDTH
|
||||
paste_position = (paste_x, self.__LINEWIDTH*3)
|
||||
background.paste(top_line, paste_position, top_line)
|
||||
|
||||
last_line = self.__bad_line(int(self.__GALLOWY*0.125))
|
||||
paste_x += int(self.__GALLOWY*0.30)
|
||||
background.paste(last_line, (paste_x, self.__LINEWIDTH*3), last_line)
|
||||
return background
|
||||
|
||||
def __draw_letter_lines(self, word: str, guessed: list, misses: int):
|
||||
letter_width = self.__LETTERLINELENGTH+self.__LETTERLINEDISTANCE
|
||||
image_width = letter_width*len(word)
|
||||
image_size = (image_width, self.__LETTERLINELENGTH+self.__LINEWIDTH*3)
|
||||
letter_lines = Image.new("RGBA", image_size, color=(0, 0, 0, 0))
|
||||
for i, letter in enumerate(word):
|
||||
line = self.__bad_line(self.__LETTERLINELENGTH, True)
|
||||
paste_x = i*(self.__LETTERLINELENGTH+self.__LETTERLINEDISTANCE)
|
||||
paste_position = (paste_x, self.__LETTERLINELENGTH)
|
||||
letter_lines.paste(line, paste_position, line)
|
||||
if guessed[i]:
|
||||
letter_drawing = self.__bad_text(letter, True)
|
||||
letter_width = self.__FONT.getsize(letter)[0]
|
||||
letter_x = i*(self.__LETTERLINELENGTH+self.__LETTERLINEDISTANCE)
|
||||
letter_x -= (letter_width//2)
|
||||
letter_x += (self.__LETTERLINELENGTH//2)+(self.__LINEWIDTH*2)
|
||||
letter_lines.paste(
|
||||
letter_drawing,
|
||||
(letter_x, 0),
|
||||
letter_drawing
|
||||
)
|
||||
elif misses == 6:
|
||||
letter_drawing = self.__bad_text(letter, True, (242, 66, 54))
|
||||
letter_width = self.__FONT.getsize(letter)[0]
|
||||
letter_x = i*(self.__LETTERLINELENGTH+self.__LETTERLINEDISTANCE)
|
||||
letter_x -= (letter_width//2)
|
||||
letter_x += (self.__LETTERLINELENGTH//2)+(self.__LINEWIDTH*2)
|
||||
letter_lines.paste(
|
||||
letter_drawing,
|
||||
(letter_x, 0),
|
||||
letter_drawing
|
||||
)
|
||||
|
||||
return letter_lines
|
||||
|
||||
def __shortest_dist(self, positions: list, new_position: tuple):
|
||||
shortest_dist = math.inf
|
||||
for i, j in positions:
|
||||
x_distance = abs(i-new_position[0])
|
||||
y_distance = abs(j-new_position[1])
|
||||
dist = math.sqrt(x_distance**2+y_distance**2)
|
||||
if shortest_dist > dist:
|
||||
shortest_dist = dist
|
||||
return shortest_dist
|
||||
|
||||
def __draw_misses(self, guesses: list, word: str):
|
||||
background = Image.new("RGBA", (600, 400), color=(0, 0, 0, 0))
|
||||
pos = []
|
||||
for guess in guesses:
|
||||
if guess not in word:
|
||||
placed = False
|
||||
while not placed:
|
||||
letter = self.__bad_text(guess, True)
|
||||
w, h = self.__FONT.getsize(guess)
|
||||
x = random.randint(0, 600-w)
|
||||
y = random.randint(0, 400-h)
|
||||
if self.__shortest_dist(pos, (x, y)) > 70:
|
||||
pos.append((x, y))
|
||||
background.paste(letter, (x, y), letter)
|
||||
placed = True
|
||||
return background
|
||||
|
||||
def draw_image(self, channel: str):
|
||||
"""
|
||||
Draw a hangman Image.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
channel: str
|
||||
The id of the channel the game is in.
|
||||
"""
|
||||
self.__bot.log("Drawing hangman image", channel)
|
||||
game = self.__bot.database["hangman games"].find_one({"_id": channel})
|
||||
|
||||
random.seed(game["game ID"])
|
||||
|
||||
background = Image.open("gwendolyn/resources/paper.jpg")
|
||||
gallow = self.__draw_gallows()
|
||||
man = self.__draw_man(game["misses"], game["game ID"])
|
||||
|
||||
random.seed(game["game ID"])
|
||||
letter_line_parameters = [game["word"], game["guessed"], game["misses"]]
|
||||
letter_lines = self.__draw_letter_lines(*letter_line_parameters)
|
||||
|
||||
random.seed(game["game ID"])
|
||||
misses = self.__draw_misses(game["guessed letters"], game["word"])
|
||||
|
||||
background.paste(gallow, (100, 100), gallow)
|
||||
background.paste(man, (300, 210), man)
|
||||
background.paste(letter_lines, (120, 840), letter_lines)
|
||||
background.paste(misses, (600, 150), misses)
|
||||
|
||||
misses_text = self.__bad_text("MISSES", False)
|
||||
misses_text_width = misses_text.size[0]
|
||||
background.paste(misses_text, (850-misses_text_width//2, 50), misses_text)
|
||||
|
||||
board_path = f"gwendolyn/resources/games/hangman_boards/hangman_board{channel}.png"
|
||||
background.save(board_path)
|
||||
@@ -28,24 +28,24 @@ class HexGame():
|
||||
await ctx.send("You can't surrender when you're not a player.")
|
||||
else:
|
||||
opponent = (players.index(user) + 1) % 2
|
||||
opponentName = self.bot.databaseFuncs.getName(players[opponent])
|
||||
opponent_name = self.bot.database_funcs.get_name(players[opponent])
|
||||
self.bot.database["hex games"].update_one({"_id":channel},{"$set":{"winner":opponent + 1}})
|
||||
await ctx.send(f"{ctx.author.display_name} surrendered")
|
||||
|
||||
with open(f"resources/games/oldImages/hex{channel}", "r") as f:
|
||||
oldImage = await ctx.channel.fetch_message(int(f.read()))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{channel}", "r") as f:
|
||||
old_image = await ctx.channel.fetch_message(int(f.read()))
|
||||
|
||||
if oldImage is not None:
|
||||
await oldImage.delete()
|
||||
if old_image is not None:
|
||||
await old_image.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))
|
||||
file_path = f"gwendolyn/resources/games/hex_boards/board{channel}.png"
|
||||
old_image = await ctx.channel.send(file = discord.File(file_path))
|
||||
|
||||
with open(f"resources/games/oldImages/hex{channel}", "w") as f:
|
||||
f.write(str(oldImage.id))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{channel}", "w") as f:
|
||||
f.write(str(old_image.id))
|
||||
|
||||
self.bot.database["hex games"].delete_one({"_id":channel})
|
||||
|
||||
@@ -71,26 +71,26 @@ class HexGame():
|
||||
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")
|
||||
gwendolyn_turn = (opponent == f"#{self.bot.user.id}")
|
||||
opponent_name = self.bot.database_funcs.get_name(opponent)
|
||||
await ctx.send(f"The color of the players were swapped. It is now {opponent_name}'s turn")
|
||||
|
||||
with open(f"resources/games/oldImages/hex{channel}", "r") as f:
|
||||
oldImage = await ctx.channel.fetch_message(int(f.read()))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{channel}", "r") as f:
|
||||
old_image = await ctx.channel.fetch_message(int(f.read()))
|
||||
|
||||
if oldImage is not None:
|
||||
await oldImage.delete()
|
||||
if old_image is not None:
|
||||
await old_image.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))
|
||||
file_path = f"gwendolyn/resources/games/hex_boards/board{channel}.png"
|
||||
old_image = await ctx.channel.send(file = discord.File(file_path))
|
||||
|
||||
with open(f"resources/games/oldImages/hex{channel}", "w") as f:
|
||||
f.write(str(oldImage.id))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{channel}", "w") as f:
|
||||
f.write(str(old_image.id))
|
||||
|
||||
if gwendoTurn:
|
||||
if gwendolyn_turn:
|
||||
await self.hexAI(ctx)
|
||||
|
||||
# Starts the game
|
||||
@@ -100,109 +100,109 @@ class HexGame():
|
||||
channel = str(ctx.channel_id)
|
||||
game = self.bot.database["hex games"].find_one({"_id":channel})
|
||||
|
||||
startedGame = False
|
||||
canStart = True
|
||||
started_game = False
|
||||
can_start = 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
|
||||
send_message = "There's already a hex game going on in this channel"
|
||||
log_message = "There was already a game going on"
|
||||
can_start = False
|
||||
else:
|
||||
if type(opponent) == int:
|
||||
# Opponent is Gwendolyn
|
||||
if opponent in range(1, 6):
|
||||
opponentName = "Gwendolyn"
|
||||
opponent_name = "Gwendolyn"
|
||||
difficulty = int(opponent)
|
||||
diffText = f" with difficulty {difficulty}"
|
||||
difficulty_text = 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
|
||||
send_message = "Difficulty doesn't exist"
|
||||
log_message = "They tried to play against a difficulty that doesn't exist"
|
||||
can_start = 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"
|
||||
opponent_name = "Gwendolyn"
|
||||
difficulty = 2
|
||||
diffText = f" with difficulty {difficulty}"
|
||||
difficulty_text = 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
|
||||
send_message = "You can't challenge a bot!"
|
||||
log_message = "They tried to challenge a bot"
|
||||
can_start = False
|
||||
else:
|
||||
# Opponent is another player
|
||||
if ctx.author != opponent:
|
||||
opponentName = opponent.display_name
|
||||
opponent_name = opponent.display_name
|
||||
opponent = f"#{opponent.id}"
|
||||
difficulty = 5
|
||||
diffText = ""
|
||||
difficulty_text = ""
|
||||
else:
|
||||
sendMessage = "You can't play against yourself"
|
||||
logMessage = "They tried to play against themself"
|
||||
canStart = False
|
||||
send_message = "You can't play against yourself"
|
||||
log_message = "They tried to play against themself"
|
||||
can_start = False
|
||||
else:
|
||||
canStart = False
|
||||
logMessage = f"Opponent was neither int or member. It was {type(opponent)}"
|
||||
sendMessage = "Something went wrong"
|
||||
can_start = False
|
||||
log_message = f"Opponent was neither int or member. It was {type(opponent)}"
|
||||
send_message = "Something went wrong"
|
||||
|
||||
if canStart:
|
||||
if can_start:
|
||||
# board is 11x11
|
||||
board = [[0 for i in range(self.BOARDWIDTH)] for j in range(self.BOARDWIDTH)]
|
||||
players = [user, opponent]
|
||||
random.shuffle(players) # random starting player
|
||||
gameHistory = []
|
||||
|
||||
newGame = {"_id":channel,"board":board, "winner":0,
|
||||
new_game = {"_id":channel,"board":board, "winner":0,
|
||||
"players":players, "turn":1, "difficulty":difficulty, "gameHistory":gameHistory}
|
||||
|
||||
self.bot.database["hex games"].insert_one(newGame)
|
||||
self.bot.database["hex games"].insert_one(new_game)
|
||||
|
||||
# draw the board
|
||||
self.draw.drawBoard(channel)
|
||||
|
||||
gwendoTurn = (players[0] == f"#{self.bot.user.id}")
|
||||
startedGame = True
|
||||
gwendolyn_turn = (players[0] == f"#{self.bot.user.id}")
|
||||
started_game = True
|
||||
|
||||
turnName = self.bot.databaseFuncs.getName(players[0])
|
||||
sendMessage = f"Started Hex game against {opponentName}{diffText}. It's {turnName}'s turn"
|
||||
logMessage = "Game started"
|
||||
turn_name = self.bot.database_funcs.get_name(players[0])
|
||||
send_message = f"Started Hex game against {opponent_name}{difficulty_text}. It's {turn_name}'s turn"
|
||||
log_message = "Game started"
|
||||
|
||||
await ctx.send(sendMessage)
|
||||
self.bot.log(logMessage)
|
||||
await ctx.send(send_message)
|
||||
self.bot.log(log_message)
|
||||
|
||||
if startedGame:
|
||||
filePath = f"resources/games/hexBoards/board{ctx.channel_id}.png"
|
||||
newImage = await ctx.channel.send(file = discord.File(filePath))
|
||||
if started_game:
|
||||
file_path = f"gwendolyn/resources/games/hex_boards/board{ctx.channel_id}.png"
|
||||
new_image = await ctx.channel.send(file = discord.File(file_path))
|
||||
|
||||
with open(f"resources/games/oldImages/hex{ctx.channel_id}", "w") as f:
|
||||
f.write(str(newImage.id))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{ctx.channel_id}", "w") as f:
|
||||
f.write(str(new_image.id))
|
||||
|
||||
if gwendoTurn:
|
||||
if gwendolyn_turn:
|
||||
await self.hexAI(ctx)
|
||||
|
||||
# Places a piece at the given location and checks things afterwards
|
||||
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
|
||||
placed_piece = False
|
||||
|
||||
if game == None:
|
||||
sendMessage = "There's no game in this channel"
|
||||
send_message = "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."
|
||||
send_message = "The position must be a letter followed by a number."
|
||||
self.bot.log(f"The position was not valid, {position}")
|
||||
else:
|
||||
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])}."
|
||||
send_message = f"You can't place when you're not in the game. The game's players are: {self.bot.database_funcs.get_name(game['players'][0])} and {self.bot.database_funcs.get_name(game['players'][1])}."
|
||||
self.bot.log("They aren't in the game")
|
||||
elif players[game["turn"]-1] != user:
|
||||
sendMessage = "It's not your turn"
|
||||
send_message = "It's not your turn"
|
||||
self.bot.log("It wasn't their turn")
|
||||
else:
|
||||
player = game["turn"]
|
||||
@@ -215,7 +215,7 @@ class HexGame():
|
||||
|
||||
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.")
|
||||
send_message = ("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}})
|
||||
@@ -227,50 +227,50 @@ class HexGame():
|
||||
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)
|
||||
game_won = False
|
||||
send_message = self.bot.database_funcs.get_name(game["players"][player-1])+" placed at "+position.upper()+". It's now "+self.bot.database_funcs.get_name(game["players"][turn-1])+"'s turn."# The score is "+str(score)
|
||||
|
||||
else: # Congratulations!
|
||||
gameWon = True
|
||||
game_won = 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!"
|
||||
send_message = self.bot.database_funcs.get_name(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."
|
||||
win_amount = game["difficulty"]*10
|
||||
send_message += " Adding "+str(win_amount)+" 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
|
||||
gwendolyn_turn = False
|
||||
if game["players"][turn-1] == f"#{self.bot.user.id}":
|
||||
self.bot.log("It's Gwendolyn's turn")
|
||||
gwendoTurn = True
|
||||
gwendolyn_turn = True
|
||||
|
||||
placedPiece = True
|
||||
placed_piece = True
|
||||
|
||||
if user == f"#{self.bot.user.id}":
|
||||
await ctx.channel.send(sendMessage)
|
||||
await ctx.channel.send(send_message)
|
||||
else:
|
||||
await ctx.send(sendMessage)
|
||||
await ctx.send(send_message)
|
||||
|
||||
if placedPiece:
|
||||
if placed_piece:
|
||||
# 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()))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{channel}", "r") as f:
|
||||
old_image = await ctx.channel.fetch_message(int(f.read()))
|
||||
|
||||
if oldImage is not None:
|
||||
await oldImage.delete()
|
||||
if old_image is not None:
|
||||
await old_image.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))
|
||||
file_path = f"gwendolyn/resources/games/hex_boards/board{channel}.png"
|
||||
old_image = await ctx.channel.send(file = discord.File(file_path))
|
||||
|
||||
if gameWon:
|
||||
if game_won:
|
||||
self.bot.log("Dealing with the winning player")
|
||||
game = self.bot.database["hex games"].find_one({"_id":channel})
|
||||
|
||||
@@ -281,10 +281,10 @@ class HexGame():
|
||||
|
||||
self.bot.database["hex games"].delete_one({"_id":channel})
|
||||
else:
|
||||
with open(f"resources/games/oldImages/hex{channel}", "w") as f:
|
||||
f.write(str(oldImage.id))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{channel}", "w") as f:
|
||||
f.write(str(old_image.id))
|
||||
|
||||
if gwendoTurn:
|
||||
if gwendolyn_turn:
|
||||
await self.hexAI(ctx)
|
||||
|
||||
# Returns a board where the placement has ocurred
|
||||
@@ -314,14 +314,14 @@ class HexGame():
|
||||
game = self.bot.database["hex games"].find_one({"_id":channel})
|
||||
|
||||
if user not in game["players"]:
|
||||
sendMessage = "You're not a player in the game"
|
||||
send_message = "You're not a player in the game"
|
||||
elif len(game["gameHistory"]) == 0:
|
||||
sendMessage = "You can't undo nothing"
|
||||
send_message = "You can't undo nothing"
|
||||
elif user != game["players"][(game["turn"] % 2)]: # If it's not your turn
|
||||
sendMessage = "It's not your turn"
|
||||
send_message = "It's not your turn"
|
||||
else:
|
||||
turn = game["turn"]
|
||||
self.bot.log("Undoing {}'s last move".format(self.bot.databaseFuncs.getName(user)))
|
||||
self.bot.log("Undoing {}'s last move".format(self.bot.database_funcs.get_name(user)))
|
||||
|
||||
lastMove = game["gameHistory"].pop()
|
||||
game["board"][lastMove[0]][lastMove[1]] = 0
|
||||
@@ -332,25 +332,25 @@ class HexGame():
|
||||
|
||||
# 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}"
|
||||
send_message = f"You undid your last move at {lastMove}"
|
||||
undid = True
|
||||
|
||||
await ctx.send(sendMessage)
|
||||
await ctx.send(send_message)
|
||||
if undid:
|
||||
with open(f"resources/games/oldImages/hex{channel}", "r") as f:
|
||||
oldImage = await ctx.channel.fetch_message(int(f.read()))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{channel}", "r") as f:
|
||||
old_image = await ctx.channel.fetch_message(int(f.read()))
|
||||
|
||||
if oldImage is not None:
|
||||
await oldImage.delete()
|
||||
if old_image is not None:
|
||||
await old_image.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))
|
||||
file_path = f"gwendolyn/resources/games/hex_boards/board{channel}.png"
|
||||
old_image = await ctx.channel.send(file = discord.File(file_path))
|
||||
|
||||
with open(f"resources/games/oldImages/hex{channel}", "w") as f:
|
||||
f.write(str(oldImage.id))
|
||||
with open(f"gwendolyn/resources/games/old_images/hex{channel}", "w") as f:
|
||||
f.write(str(old_image.id))
|
||||
|
||||
|
||||
# Plays as the AI
|
||||
@@ -417,20 +417,20 @@ class HexGame():
|
||||
return scores[2]-scores[1], winner
|
||||
|
||||
|
||||
def minimaxHex(self, board, depth, alpha, beta, maximizingPlayer):
|
||||
def minimaxHex(self, board, depth, alpha, beta, maximizing_player):
|
||||
# The depth is how many moves ahead the computer checks. This value is the difficulty.
|
||||
if depth == 0 or 0 not in sum(board,[]):
|
||||
score = self.evaluateBoard(board)[0]
|
||||
return score
|
||||
# if final depth is not reached, look another move ahead:
|
||||
if maximizingPlayer: # red player predicts next move
|
||||
if maximizing_player: # red player predicts next move
|
||||
maxEval = -math.inf
|
||||
possiblePlaces = [i for i,v in enumerate(sum(board,[])) if v == 0]
|
||||
#self.bot.log("Judging a red move at depth {}".format(depth))
|
||||
for i in possiblePlaces:
|
||||
testBoard = copy.deepcopy(board)
|
||||
testBoard[i // self.BOARDWIDTH][i % self.BOARDWIDTH] = 1 # because maximizingPlayer is Red which is number 1
|
||||
evaluation = self.minimaxHex(testBoard,depth-1,alpha,beta,False)
|
||||
test_board = copy.deepcopy(board)
|
||||
test_board[i // self.BOARDWIDTH][i % self.BOARDWIDTH] = 1 # because maximizing_player is Red which is number 1
|
||||
evaluation = self.minimaxHex(test_board,depth-1,alpha,beta,False)
|
||||
maxEval = max(maxEval, evaluation)
|
||||
alpha = max(alpha, evaluation)
|
||||
if beta <= alpha:
|
||||
@@ -442,9 +442,9 @@ class HexGame():
|
||||
possiblePlaces = [i for i,v in enumerate(sum(board,[])) if v == 0]
|
||||
#self.bot.log("Judging a blue move at depth {}".format(depth))
|
||||
for i in possiblePlaces:
|
||||
testBoard = copy.deepcopy(board)
|
||||
testBoard[i // self.BOARDWIDTH][i % self.BOARDWIDTH] = 2 # because minimizingPlayer is Blue which is number 2
|
||||
evaluation = self.minimaxHex(testBoard,depth-1,alpha,beta,True)
|
||||
test_board = copy.deepcopy(board)
|
||||
test_board[i // self.BOARDWIDTH][i % self.BOARDWIDTH] = 2 # because minimizingPlayer is Blue which is number 2
|
||||
evaluation = self.minimaxHex(test_board,depth-1,alpha,beta,True)
|
||||
minEval = min(minEval, evaluation)
|
||||
beta = min(beta, evaluation)
|
||||
if beta <= alpha:
|
||||
@@ -474,7 +474,7 @@ class DrawHex():
|
||||
self.HEXAGONHEIGHT = 1.5 * self.SIDELENGTH
|
||||
self.FONTSIZE = 45
|
||||
self.TEXTCOLOR = (0,0,0)
|
||||
self.FONT = ImageFont.truetype('resources/fonts/futura-bold.ttf', self.FONTSIZE)
|
||||
self.FONT = ImageFont.truetype('gwendolyn/resources/fonts/futura-bold.ttf', self.FONTSIZE)
|
||||
|
||||
self.LINETHICKNESS = 15
|
||||
self.HEXTHICKNESS = 6 # This is half the width of the background lining between every hex
|
||||
@@ -490,7 +490,7 @@ class DrawHex():
|
||||
self.COLYTHICKNESS = self.COLHEXTHICKNESS * math.sin(math.pi/6)
|
||||
# The Name display things:
|
||||
self.NAMESIZE = 60
|
||||
self.NAMEFONT = ImageFont.truetype('resources/fonts/futura-bold.ttf', self.NAMESIZE)
|
||||
self.NAMEFONT = ImageFont.truetype('gwendolyn/resources/fonts/futura-bold.ttf', self.NAMESIZE)
|
||||
self.XNAME = {1:175, 2:self.CANVASWIDTH-100}
|
||||
self.YNAME = {1:self.CANVASHEIGHT-150, 2:150}
|
||||
self.NAMEHEXPADDING = 90
|
||||
@@ -556,7 +556,7 @@ class DrawHex():
|
||||
game = self.bot.database["hex games"].find_one({"_id":channel})
|
||||
|
||||
for p in [1,2]:
|
||||
playername = self.bot.databaseFuncs.getName(game["players"][p-1])
|
||||
playername = self.bot.database_funcs.get_name(game["players"][p-1])
|
||||
# Draw name
|
||||
x = self.XNAME[p]
|
||||
x -= self.NAMEFONT.getsize(playername)[0] if p==2 else 0 # player2's name is right-aligned
|
||||
@@ -573,13 +573,13 @@ class DrawHex():
|
||||
(x, y+self.SMALLSIDELENGTH),
|
||||
],fill = self.PIECECOLOR[p])
|
||||
|
||||
im.save("resources/games/hexBoards/board"+channel+".png")
|
||||
im.save("gwendolyn/resources/games/hex_boards/board"+channel+".png")
|
||||
|
||||
|
||||
|
||||
|
||||
def drawHexPlacement(self, channel,player,position):
|
||||
FILEPATH = "resources/games/hexBoards/board"+channel+".png"
|
||||
FILEPATH = "gwendolyn/resources/games/hex_boards/board"+channel+".png"
|
||||
self.bot.log(f"Drawing a newly placed hex. Filename: board{channel}.png")
|
||||
|
||||
# Translates position
|
||||
@@ -589,7 +589,7 @@ class DrawHex():
|
||||
row = int(position[1:])-1
|
||||
|
||||
# Find the coordinates for the filled hex drawing
|
||||
hexCoords = [
|
||||
hex_coords = [
|
||||
(self.BOARDCOORDINATES[row][column][0]+self.COLXTHICKNESS, self.BOARDCOORDINATES[row][column][1] + self.COLYTHICKNESS),
|
||||
(self.BOARDCOORDINATES[row][column][0]+self.HEXAGONWIDTH/2, self.BOARDCOORDINATES[row][column][1]-0.5*self.SIDELENGTH + self.COLHEXTHICKNESS),
|
||||
(self.BOARDCOORDINATES[row][column][0]+self.HEXAGONWIDTH-self.COLXTHICKNESS, self.BOARDCOORDINATES[row][column][1] + self.COLYTHICKNESS),
|
||||
@@ -603,7 +603,7 @@ class DrawHex():
|
||||
with Image.open(FILEPATH) as im:
|
||||
d = ImageDraw.Draw(im,"RGBA")
|
||||
# Draws the hex piece
|
||||
d.polygon(hexCoords,fill = self.PIECECOLOR[player], outline = self.BETWEENCOLOR)
|
||||
d.polygon(hex_coords,fill = self.PIECECOLOR[player], outline = self.BETWEENCOLOR)
|
||||
|
||||
# Save
|
||||
im.save(FILEPATH)
|
||||
@@ -611,7 +611,7 @@ class DrawHex():
|
||||
self.bot.log("Error drawing new hex on board (error code 1541")
|
||||
|
||||
def drawSwap(self, channel):
|
||||
FILEPATH = "resources/games/hexBoards/board"+channel+".png"
|
||||
FILEPATH = "gwendolyn/resources/games/hex_boards/board"+channel+".png"
|
||||
game = self.bot.database["hex games"].find_one({"_id":channel})
|
||||
# Opens the image
|
||||
try:
|
||||
@@ -620,7 +620,7 @@ class DrawHex():
|
||||
|
||||
# Write player names and color
|
||||
for p in [1,2]:
|
||||
playername = self.bot.databaseFuncs.getName(game["players"][p%2])
|
||||
playername = self.bot.database_funcs.get_name(game["players"][p%2])
|
||||
|
||||
x = self.XNAME[p]
|
||||
x -= self.NAMEFONT.getsize(playername)[0] if p==2 else 0 # player2's name is right-aligned
|
||||
@@ -0,0 +1,286 @@
|
||||
"""
|
||||
Contains functions relating to invest commands.
|
||||
|
||||
*Classes*
|
||||
---------
|
||||
Invest
|
||||
Contains all the code for the invest commands.
|
||||
"""
|
||||
import discord # Used for embeds
|
||||
from discord_slash.context import SlashContext # Used for type hints
|
||||
|
||||
|
||||
class Invest():
|
||||
"""
|
||||
Contains all the invest functions.
|
||||
|
||||
*Methods*
|
||||
---------
|
||||
getPrice(symbol: str) -> int
|
||||
getPortfolio(user: str) -> str
|
||||
buyStock(user: str, stock: str: buyAmount: int) -> str
|
||||
sellStock(user: str, stock: str, buyAmount: int) -> str
|
||||
parseInvest(ctx: discord_slash.context.SlashContext,
|
||||
parameters: str)
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the class."""
|
||||
self.bot = bot
|
||||
|
||||
def getPrice(self, symbol: str):
|
||||
"""
|
||||
Get the price of a stock.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
symbol: str
|
||||
The symbol of the stock to get the price of.
|
||||
|
||||
*Returns*
|
||||
---------
|
||||
price: int
|
||||
The price of the stock.
|
||||
"""
|
||||
res = self.bot.finnhub_client.quote(symbol.upper())
|
||||
if res == {}:
|
||||
return 0
|
||||
else:
|
||||
return int(res["c"] * 100)
|
||||
|
||||
def getPortfolio(self, user: str):
|
||||
"""
|
||||
Get the stock portfolio of a user.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
user: str
|
||||
The id of the user to get the portfolio of.
|
||||
|
||||
*Returns*
|
||||
---------
|
||||
portfolio: str
|
||||
The portfolio.
|
||||
"""
|
||||
investments_database = self.bot.database["investments"]
|
||||
userInvestments = investments_database.find_one({"_id": user})
|
||||
|
||||
user_name = self.bot.database_funcs.get_name(user)
|
||||
|
||||
if userInvestments in [None, {}]:
|
||||
return f"{user_name} does not have a stock portfolio."
|
||||
else:
|
||||
portfolio = f"**Stock portfolio for {user_name}**"
|
||||
|
||||
for key, value in list(userInvestments["investments"].items()):
|
||||
purchaseValue = value["purchased for"]
|
||||
stockPrice = self.getPrice(key)
|
||||
valueAtPurchase = value["value at purchase"]
|
||||
purchasedStock = value["purchased"]
|
||||
valueChange = (stockPrice / valueAtPurchase)
|
||||
currentValue = int(valueChange * purchasedStock)
|
||||
portfolio += f"\n**{key}**: ___{currentValue} GwendoBucks___"
|
||||
|
||||
if purchaseValue != "?":
|
||||
portfolio += f" (purchased for {purchaseValue})"
|
||||
|
||||
return portfolio
|
||||
|
||||
def buyStock(self, user: str, stock: str, buyAmount: int):
|
||||
"""
|
||||
Buy an amount of a specific stock.
|
||||
|
||||
*Paramaters*
|
||||
------------
|
||||
user: str
|
||||
The id of the user buying.
|
||||
stock: str
|
||||
The symbol of the stock to buy.
|
||||
buyAmount: int
|
||||
The amount of GwendoBucks to use to buy the stock.
|
||||
|
||||
*Returns*
|
||||
---------
|
||||
send_message: str
|
||||
The message to return to the user.
|
||||
"""
|
||||
if buyAmount < 100:
|
||||
return "You cannot buy stocks for less than 100 GwendoBucks"
|
||||
elif self.bot.money.checkBalance(user) < buyAmount:
|
||||
return "You don't have enough money for that"
|
||||
elif self.getPrice(stock) <= 0:
|
||||
return f"{stock} is not traded on the american market."
|
||||
else:
|
||||
investments_database = self.bot.database["investments"]
|
||||
stockPrice = self.getPrice(stock)
|
||||
userInvestments = investments_database.find_one({"_id": user})
|
||||
|
||||
self.bot.money.addMoney(user, -1*buyAmount)
|
||||
stock = stock.upper()
|
||||
|
||||
if userInvestments is not None:
|
||||
userInvestments = userInvestments["investments"]
|
||||
if stock in userInvestments:
|
||||
value = userInvestments[stock]
|
||||
valueChange = (stockPrice / value["value at purchase"])
|
||||
currentValue = int(valueChange * value["purchased"])
|
||||
newAmount = currentValue + buyAmount
|
||||
|
||||
value_path = f"investments.{stock}.value at purchase"
|
||||
updater = {"$set": {value_path: stockPrice}}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
|
||||
purchased_path = f"investments.{stock}.purchased"
|
||||
updater = {"$set": {purchased_path: newAmount}}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
|
||||
if value["purchased for"] != "?":
|
||||
purchasedFor_path = f"investments.{stock}.purchased for"
|
||||
updater = {"$set": {purchasedFor_path: buyAmount}}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
else:
|
||||
updater = {
|
||||
"$set": {
|
||||
"investments.{stock}": {
|
||||
"purchased": buyAmount,
|
||||
"value at purchase": stockPrice,
|
||||
"purchased for": buyAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
else:
|
||||
new_user = {
|
||||
"_id": user,
|
||||
"investments": {
|
||||
stock: {
|
||||
"purchased": buyAmount,
|
||||
"value at purchase": stockPrice,
|
||||
"purchased for": buyAmount
|
||||
}
|
||||
}
|
||||
}
|
||||
investments_database.insert_one(new_user)
|
||||
|
||||
user_name = self.bot.database_funcs.get_name(user)
|
||||
send_message = "{} bought {} GwendoBucks worth of {} stock"
|
||||
send_message = send_message.format(user_name, buyAmount, stock)
|
||||
return send_message
|
||||
|
||||
def sellStock(self, user: str, stock: str, sellAmount: int):
|
||||
"""
|
||||
Sell an amount of a specific stock.
|
||||
|
||||
*Paramaters*
|
||||
------------
|
||||
user: str
|
||||
The id of the user selling.
|
||||
stock: str
|
||||
The symbol of the stock to sell.
|
||||
buyAmount: int
|
||||
The amount of GwendoBucks to sell for.
|
||||
|
||||
*Returns*
|
||||
---------
|
||||
send_message: str
|
||||
The message to return to the user.
|
||||
"""
|
||||
if sellAmount <= 0:
|
||||
return "no"
|
||||
else:
|
||||
investments_database = self.bot.database["investments"]
|
||||
user_data = investments_database.find_one({"_id": user})
|
||||
userInvestments = user_data["investments"]
|
||||
|
||||
stock = stock.upper()
|
||||
|
||||
if userInvestments is not None and stock in userInvestments:
|
||||
value = userInvestments[stock]
|
||||
stockPrice = self.getPrice(stock)
|
||||
priceChange = (stockPrice / value["value at purchase"])
|
||||
purchasedAmount = int(priceChange * value["purchased"])
|
||||
purchased_path = f"investments.{stock}.purchased"
|
||||
updater = {"$set": {purchased_path: purchasedAmount}}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
valueAtPurchase_path = f"investments.{stock}.value at purchase"
|
||||
updater = {"$set": {valueAtPurchase_path: stockPrice}}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
if value["purchased"] >= sellAmount:
|
||||
self.bot.money.addMoney(user, sellAmount)
|
||||
if sellAmount < value["purchased"]:
|
||||
purchased_path = f"investments.{stock}.purchased"
|
||||
updater = {"$inc": {purchased_path: -sellAmount}}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
|
||||
purchasedFor_path = f"investments.{stock}.purchased for"
|
||||
updater = {"$set": {purchasedFor_path: "?"}}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
else:
|
||||
updater = {"$unset": {f"investments.{stock}": ""}}
|
||||
investments_database.update_one({"_id": user}, updater)
|
||||
|
||||
user_name = self.bot.database_funcs.get_name(user)
|
||||
send_message = "{} sold {} GwendoBucks worth of {} stock"
|
||||
return send_message.format(user_name, sellAmount, stock)
|
||||
else:
|
||||
return f"You don't have enough {stock} stocks to do that"
|
||||
else:
|
||||
return f"You don't have any {stock} stock"
|
||||
|
||||
async def parseInvest(self, ctx: SlashContext, parameters: str):
|
||||
"""
|
||||
Parse an invest command. TO BE DELETED.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
ctx: discord_slash.context.SlashContext
|
||||
The context of the slash command.
|
||||
parameters: str
|
||||
The parameters of the command.
|
||||
"""
|
||||
await self.bot.defer(ctx)
|
||||
user = f"#{ctx.author.id}"
|
||||
|
||||
if parameters.startswith("check"):
|
||||
commands = parameters.split(" ")
|
||||
if len(commands) == 1:
|
||||
response = self.getPortfolio(user)
|
||||
else:
|
||||
price = self.getPrice(commands[1])
|
||||
if price == 0:
|
||||
response = "{} is not traded on the american market."
|
||||
response = response.format(commands[0].upper())
|
||||
else:
|
||||
price = f"{price:,}".replace(",", ".")
|
||||
response = self.bot.long_strings["Stock value"]
|
||||
response = response.format(commands[1].upper(), price)
|
||||
|
||||
elif parameters.startswith("buy"):
|
||||
commands = parameters.split(" ")
|
||||
if len(commands) == 3:
|
||||
response = self.buyStock(user, commands[1], int(commands[2]))
|
||||
else:
|
||||
response = self.bot.long_strings["Stock parameters"]
|
||||
|
||||
elif parameters.startswith("sell"):
|
||||
commands = parameters.split(" ")
|
||||
if len(commands) == 3:
|
||||
response = self.sellStock(user, commands[1], int(commands[2]))
|
||||
else:
|
||||
response = self.bot.long_strings["Stock parameters"]
|
||||
|
||||
else:
|
||||
response = "Incorrect parameters"
|
||||
|
||||
if response.startswith("**"):
|
||||
responses = response.split("\n")
|
||||
text = "\n".join(responses[1:])
|
||||
embedParams = {
|
||||
"title": responses[0],
|
||||
"description": text,
|
||||
"colour": 0x00FF00
|
||||
}
|
||||
em = discord.Embed(**embedParams)
|
||||
await ctx.send(embed=em)
|
||||
else:
|
||||
await ctx.send(response)
|
||||
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
Contains the code that deals with money.
|
||||
|
||||
*Classes*
|
||||
---------
|
||||
Money
|
||||
Deals with money.
|
||||
"""
|
||||
import discord_slash # Used for typehints
|
||||
import discord # Used for typehints
|
||||
|
||||
|
||||
class Money():
|
||||
"""
|
||||
Deals with money.
|
||||
|
||||
*Methods*
|
||||
---------
|
||||
checkBalance(user: str)
|
||||
sendBalance(ctx: discord_slash.context.SlashContext)
|
||||
addMoney(user: str, amount: int)
|
||||
giveMoney(ctx: discord_slash.context.SlashContext, user: discord.User,
|
||||
amount: int)
|
||||
|
||||
*Attributes*
|
||||
------------
|
||||
bot: Gwendolyn
|
||||
The instance of Gwendolyn
|
||||
database: pymongo.Client
|
||||
The mongo database
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the class."""
|
||||
self.bot = bot
|
||||
self.database = bot.database
|
||||
|
||||
def checkBalance(self, user: str):
|
||||
"""
|
||||
Get the account balance of a user.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
user: str
|
||||
The user to get the balance of.
|
||||
|
||||
*Returns*
|
||||
---------
|
||||
balance: int
|
||||
The balance of the user's account.
|
||||
"""
|
||||
self.bot.log("checking "+user+"'s account balance")
|
||||
|
||||
user_data = self.database["users"].find_one({"_id": user})
|
||||
|
||||
if user_data is not None:
|
||||
return user_data["money"]
|
||||
else:
|
||||
return 0
|
||||
|
||||
async def sendBalance(self, ctx: discord_slash.context.SlashContext):
|
||||
"""
|
||||
Get your own account balance.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
ctx: discord_slash.context.SlashContext
|
||||
The context of the command.
|
||||
"""
|
||||
await self.bot.defer(ctx)
|
||||
response = self.checkBalance("#"+str(ctx.author.id))
|
||||
user_name = ctx.author.display_name
|
||||
if response == 1:
|
||||
new_message = f"{user_name} has {response} GwendoBuck"
|
||||
else:
|
||||
new_message = f"{user_name} has {response} GwendoBucks"
|
||||
await ctx.send(new_message)
|
||||
|
||||
# Adds money to the account of a user
|
||||
def addMoney(self, user: str, amount: int):
|
||||
"""
|
||||
Add money to a user account.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
user: str
|
||||
The id of the user to give money.
|
||||
amount: int
|
||||
The amount to add to the user's account.
|
||||
"""
|
||||
self.bot.log("adding "+str(amount)+" to "+user+"'s account")
|
||||
|
||||
user_data = self.database["users"].find_one({"_id": user})
|
||||
|
||||
if user_data is not None:
|
||||
updater = {"$inc": {"money": amount}}
|
||||
self.database["users"].update_one({"_id": user}, updater)
|
||||
else:
|
||||
new_user = {
|
||||
"_id": user,
|
||||
"user name": self.bot.database_funcs.get_name(user),
|
||||
"money": amount
|
||||
}
|
||||
self.database["users"].insert_one(new_user)
|
||||
|
||||
# Transfers money from one user to another
|
||||
async def giveMoney(self, ctx: discord_slash.context.SlashContext,
|
||||
user: discord.User, amount: int):
|
||||
"""
|
||||
Give someone else money from your account.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
ctx: discord_slash.context.SlashContext
|
||||
The context of the command.
|
||||
user: discord.User
|
||||
The user to give money.
|
||||
amount: int
|
||||
The amount to transfer.
|
||||
"""
|
||||
await self.bot.defer(ctx)
|
||||
username = user.display_name
|
||||
if self.bot.database_funcs.get_id(username) is None:
|
||||
async for member in ctx.guild.fetch_members(limit=None):
|
||||
if member.display_name.lower() == username.lower():
|
||||
username = member.display_name
|
||||
user_id = f"#{member.id}"
|
||||
new_user = {
|
||||
"_id": user_id,
|
||||
"user name": username,
|
||||
"money": 0
|
||||
}
|
||||
self.bot.database["users"].insert_one(new_user)
|
||||
|
||||
userid = f"#{ctx.author.id}"
|
||||
user_data = self.database["users"].find_one({"_id": userid})
|
||||
targetUser = self.bot.database_funcs.get_id(username)
|
||||
|
||||
if amount <= 0:
|
||||
self.bot.log("They tried to steal")
|
||||
await ctx.send("Yeah, no. You can't do that")
|
||||
elif targetUser is None:
|
||||
self.bot.log("They weren't in the system")
|
||||
await ctx.send("The target doesn't exist")
|
||||
elif user_data is None or user_data["money"] < amount:
|
||||
self.bot.log("They didn't have enough GwendoBucks")
|
||||
await ctx.send("You don't have that many GwendoBuck")
|
||||
else:
|
||||
self.addMoney(f"#{ctx.author.id}", -1 * amount)
|
||||
self.addMoney(targetUser, amount)
|
||||
await ctx.send(f"Transferred {amount} GwendoBucks to {username}")
|
||||
@@ -0,0 +1,205 @@
|
||||
"""
|
||||
Contains code for trivia games.
|
||||
|
||||
*Classes*
|
||||
---------
|
||||
Trivia
|
||||
Contains all the code for the trivia commands.
|
||||
"""
|
||||
import urllib # Used to get data from api
|
||||
import json # Used to read data from api
|
||||
import random # Used to shuffle answers
|
||||
import asyncio # Used to sleep
|
||||
|
||||
from discord_slash.context import SlashContext # Used for type hints
|
||||
|
||||
|
||||
class Trivia():
|
||||
"""
|
||||
Contains the code for trivia games.
|
||||
|
||||
*Methods*
|
||||
---------
|
||||
triviaStart(channel: str) -> str, str, str
|
||||
triviaAnswer(user: str, channel: str, command: str) -> str
|
||||
triviaCountPoints(channel: str)
|
||||
triviaParse(ctx: SlashContext, answer: str)
|
||||
"""
|
||||
|
||||
def __init__(self, bot):
|
||||
"""Initialize the class."""
|
||||
self.bot = bot
|
||||
|
||||
def triviaStart(self, channel: str):
|
||||
"""
|
||||
Start a game of trivia.
|
||||
|
||||
Downloads a question with answers, shuffles the wrong answers
|
||||
with the correct answer and returns the questions and answers.
|
||||
Also saves the question in the database.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
channel: str
|
||||
The id of the channel to start the game in
|
||||
|
||||
*Returns*
|
||||
---------
|
||||
send_message: str
|
||||
The message to return to the user.
|
||||
"""
|
||||
triviaQuestions = self.bot.database["trivia questions"]
|
||||
question = triviaQuestions.find_one({"_id": channel})
|
||||
|
||||
self.bot.log(f"Trying to find a trivia question for {channel}")
|
||||
|
||||
if question is None:
|
||||
apiUrl = "https://opentdb.com/api.php?amount=10&type=multiple"
|
||||
with urllib.request.urlopen(apiUrl) as response:
|
||||
data = json.loads(response.read())
|
||||
|
||||
question = data["results"][0]["question"]
|
||||
self.bot.log(f"Found the question \"{question}\"")
|
||||
answers = data["results"][0]["incorrect_answers"]
|
||||
answers.append(data["results"][0]["correct_answer"])
|
||||
random.shuffle(answers)
|
||||
correctAnswer = data["results"][0]["correct_answer"]
|
||||
correctAnswer = answers.index(correctAnswer) + 97
|
||||
|
||||
newQuestion = {
|
||||
"_id": channel,
|
||||
"answer": str(chr(correctAnswer)),
|
||||
"players": {}
|
||||
}
|
||||
triviaQuestions.insert_one(newQuestion)
|
||||
|
||||
replacements = {
|
||||
"'": "\'",
|
||||
""": "\"",
|
||||
"“": "\"",
|
||||
"”": "\"",
|
||||
"é": "é"
|
||||
}
|
||||
question = data["results"][0]["question"]
|
||||
|
||||
for key, value in replacements.items():
|
||||
question = question.replace(key, value)
|
||||
|
||||
for answer in answers:
|
||||
for key, value in replacements.items():
|
||||
answer = answer.replace(key, value)
|
||||
|
||||
return question, answers, correctAnswer
|
||||
else:
|
||||
log_message = "There was already a trivia question for that channel"
|
||||
self.bot.log(log_message)
|
||||
return self.bot.long_strings["Trivia going on"], "", ""
|
||||
|
||||
def triviaAnswer(self, user: str, channel: str, command: str):
|
||||
"""
|
||||
Answer the current trivia question.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
user: str
|
||||
The id of the user who answered.
|
||||
channel: str
|
||||
The id of the channel the game is in.
|
||||
command: str
|
||||
The user's answer.
|
||||
|
||||
*Returns*
|
||||
---------
|
||||
send_message: str
|
||||
The message to send if the function failed.
|
||||
"""
|
||||
triviaQuestions = self.bot.database["trivia questions"]
|
||||
question = triviaQuestions.find_one({"_id": channel})
|
||||
|
||||
if command not in ["a", "b", "c", "d"]:
|
||||
self.bot.log("I didn't quite understand that")
|
||||
return "I didn't quite understand that"
|
||||
elif question is None:
|
||||
self.bot.log("There's no question right now")
|
||||
return "There's no question right now"
|
||||
elif user in question["players"]:
|
||||
self.bot.log(f"{user} has already answered this question")
|
||||
return f"{user} has already answered this question"
|
||||
else:
|
||||
self.bot.log(f"{user} answered the question in {channel}")
|
||||
|
||||
updater = {"$set": {f"players.{user}": command}}
|
||||
triviaQuestions.update_one({"_id": channel}, updater)
|
||||
return None
|
||||
|
||||
def triviaCountPoints(self, channel: str):
|
||||
"""
|
||||
Add money to every winner's account.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
channel: str
|
||||
The id of the channel the game is in.
|
||||
"""
|
||||
triviaQuestions = self.bot.database["trivia questions"]
|
||||
question = triviaQuestions.find_one({"_id": channel})
|
||||
|
||||
self.bot.log("Counting points for question in "+channel)
|
||||
|
||||
if question is not None:
|
||||
for player, answer in question["players"].items():
|
||||
if answer == question["answer"]:
|
||||
self.bot.money.addMoney(player, 1)
|
||||
else:
|
||||
self.bot.log("Couldn't find the questio")
|
||||
|
||||
return None
|
||||
|
||||
async def triviaParse(self, ctx: SlashContext, answer: str):
|
||||
"""
|
||||
Parse a trivia command.
|
||||
|
||||
*Parameters*
|
||||
------------
|
||||
ctx: SlashContext
|
||||
The context of the command.
|
||||
answer: str
|
||||
The answer, if any.
|
||||
"""
|
||||
await self.bot.defer(ctx)
|
||||
channelId = str(ctx.channel_id)
|
||||
if answer == "":
|
||||
question, options, correctAnswer = self.triviaStart(channelId)
|
||||
if options != "":
|
||||
results = "**"+question+"**\n"
|
||||
for x, option in enumerate(options):
|
||||
results += chr(x+97) + ") "+option+"\n"
|
||||
|
||||
await ctx.send(results)
|
||||
|
||||
await asyncio.sleep(60)
|
||||
|
||||
self.triviaCountPoints(channelId)
|
||||
|
||||
delete_gameParams = ["trivia questions", channelId]
|
||||
self.bot.database_funcs.delete_game(*delete_gameParams)
|
||||
|
||||
self.bot.log("Time's up for the trivia question", channelId)
|
||||
send_message = self.bot.long_strings["Trivia time up"]
|
||||
format_parameters = [chr(correctAnswer), options[correctAnswer-97]]
|
||||
send_message = send_message.format(*format_parameters)
|
||||
await ctx.send(send_message)
|
||||
else:
|
||||
await ctx.send(question, hidden=True)
|
||||
|
||||
elif answer in ["a", "b", "c", "d"]:
|
||||
userId = f"#{ctx.author.id}"
|
||||
response = self.triviaAnswer(userId, channelId, answer)
|
||||
if response is None:
|
||||
user_name = ctx.author.display_name
|
||||
await ctx.send(f"{user_name} answered **{answer}**")
|
||||
else:
|
||||
await ctx.send(response)
|
||||
else:
|
||||
self.bot.log("I didn't understand that", channelId)
|
||||
await ctx.send("I didn't understand that")
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
__all__ = ["LookupFuncs"]
|
||||
|
||||
from .lookupFuncs import LookupFuncs
|
||||
from .lookup_funcs import LookupFuncs
|
||||
@@ -2,7 +2,7 @@ import math
|
||||
import json
|
||||
import discord
|
||||
|
||||
from utils import cap
|
||||
from gwendolyn.utils import cap
|
||||
|
||||
|
||||
class LookupFuncs():
|
||||
@@ -29,7 +29,7 @@ class LookupFuncs():
|
||||
await ctx.send("I don't know that monster...")
|
||||
else:
|
||||
# Opens "monsters.json"
|
||||
data = json.load(open('resources/lookup/monsters.json', encoding = "utf8"))
|
||||
data = json.load(open('gwendolyn/resources/lookup/monsters.json', encoding = "utf8"))
|
||||
for monster in data:
|
||||
if "name" in monster and str(query) == monster["name"]:
|
||||
self.bot.log("Found it!")
|
||||
@@ -149,16 +149,16 @@ class LookupFuncs():
|
||||
self.bot.log("Looking up "+query)
|
||||
|
||||
# Opens "spells.json"
|
||||
data = json.load(open('resources/lookup/spells.json', encoding = "utf8"))
|
||||
data = json.load(open('gwendolyn/resources/lookup/spells.json', encoding = "utf8"))
|
||||
if query in data:
|
||||
self.bot.log("Returning spell information")
|
||||
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']}")
|
||||
send_message = (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)")
|
||||
sendMessage = "I don't think that's a spell (error code 501)"
|
||||
send_message = "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:])
|
||||
if len(send_message) > 2000:
|
||||
await ctx.send(send_message[:2000])
|
||||
await ctx.send(send_message[2000:])
|
||||
else:
|
||||
await ctx.send(sendMessage)
|
||||
await ctx.send(send_message)
|
||||
File renamed without changes.
@@ -17,7 +17,7 @@ class Generators():
|
||||
# Generates a random name
|
||||
async def nameGen(self, ctx):
|
||||
# Makes a list of all names from "names.txt"
|
||||
names = open('resources/names.txt', encoding='utf8').read()
|
||||
names = open('gwendolyn/resources/names.txt', encoding='utf8').read()
|
||||
corpus = list(names)
|
||||
|
||||
# Makes a list of pairs
|
||||
@@ -64,7 +64,7 @@ class Generators():
|
||||
if random.randint(1,10) > 1:
|
||||
try:
|
||||
new_letter = random.choice(letter_dict[chain[-2]+chain[-1]])
|
||||
except:
|
||||
except KeyError():
|
||||
new_letter = random.choice(letter_dict[chain[-1]])
|
||||
else:
|
||||
new_letter = random.choice(letter_dict[chain[-1]])
|
||||
@@ -72,15 +72,15 @@ class Generators():
|
||||
# Ends name if the name ends
|
||||
if new_letter == "\n":
|
||||
done = True
|
||||
genName = "".join(chain)
|
||||
self.bot.log("Generated "+genName[:-1])
|
||||
gen_name = "".join(chain)
|
||||
self.bot.log("Generated "+gen_name[:-1])
|
||||
|
||||
# Returns the name
|
||||
await ctx.send(genName)
|
||||
await ctx.send(gen_name)
|
||||
|
||||
# Generates a random tavern name
|
||||
async def tavernGen(self, ctx):
|
||||
# Lists first parts, second parts and third parts of tavern names
|
||||
# _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"]
|
||||
tp = [" Tavern"," Inn","","","","","","","","",""]
|
||||
@@ -8,9 +8,9 @@ class NerdShit():
|
||||
|
||||
async def wolfSearch(self,ctx,content):
|
||||
await self.bot.defer(ctx)
|
||||
fnt = ImageFont.truetype('resources/fonts/times-new-roman.ttf', 20)
|
||||
fnt = ImageFont.truetype('gwendolyn/resources/fonts/times-new-roman.ttf', 20)
|
||||
self.bot.log("Requesting data")
|
||||
bot = wolframalpha.Client(self.bot.credentials.wolfKey)
|
||||
bot = wolframalpha.Client(self.bot.credentials["wolfram_alpha_key"])
|
||||
res = bot.query(content)
|
||||
|
||||
self.bot.log("Processing data")
|
||||
@@ -38,10 +38,10 @@ class NerdShit():
|
||||
heights += [height]
|
||||
width = max(width,int(pod.img['@width']))
|
||||
if titleChucks[x][count] == "":
|
||||
placeForText = 0
|
||||
placeFor_text = 0
|
||||
else:
|
||||
placeForText = 30
|
||||
height += int(pod.img["@height"]) + 10 + placeForText
|
||||
placeFor_text = 30
|
||||
height += int(pod.img["@height"]) + 10 + placeFor_text
|
||||
|
||||
width += 10
|
||||
height += 5
|
||||
@@ -49,33 +49,33 @@ class NerdShit():
|
||||
|
||||
for count, pod in enumerate(chunk):
|
||||
response = requests.get(pod.img["@src"])
|
||||
file = open("resources/wolfTemp.png", "wb")
|
||||
file = open("gwendolyn/resources/wolfTemp.png", "wb")
|
||||
file.write(response.content)
|
||||
file.close()
|
||||
oldImage = Image.open("resources/wolfTemp.png")
|
||||
oldSize = oldImage.size
|
||||
old_image = Image.open("gwendolyn/resources/wolfTemp.png")
|
||||
oldSize = old_image.size
|
||||
if titleChucks[x][count] == "":
|
||||
placeForText = 0
|
||||
placeFor_text = 0
|
||||
else:
|
||||
placeForText = 30
|
||||
newSize = (width,int(oldSize[1]+10+placeForText))
|
||||
newImage = Image.new("RGB",newSize,color=(255,255,255))
|
||||
newImage.paste(oldImage, (int((int(oldSize[0]+10)-oldSize[0])/2),int(((newSize[1]-placeForText)-oldSize[1])/2)+placeForText))
|
||||
placeFor_text = 30
|
||||
newSize = (width,int(oldSize[1]+10+placeFor_text))
|
||||
new_image = Image.new("RGB",newSize,color=(255,255,255))
|
||||
new_image.paste(old_image, (int((int(oldSize[0]+10)-oldSize[0])/2),int(((newSize[1]-placeFor_text)-oldSize[1])/2)+placeFor_text))
|
||||
if titleChucks[x][count] != "":
|
||||
d = ImageDraw.Draw(newImage,"RGB")
|
||||
d = ImageDraw.Draw(new_image,"RGB")
|
||||
d.text((5,7),titleChucks[x][count],font=fnt,fill=(150,150,150))
|
||||
|
||||
wolfImage.paste(newImage,(0,heights[count]))
|
||||
newImage.close()
|
||||
oldImage.close()
|
||||
wolfImage.paste(new_image,(0,heights[count]))
|
||||
new_image.close()
|
||||
old_image.close()
|
||||
count += 1
|
||||
|
||||
wolfImage.save("resources/wolf.png")
|
||||
wolfImage.save("gwendolyn/resources/wolf.png")
|
||||
wolfImage.close()
|
||||
await ctx.channel.send(file = discord.File("resources/wolf.png"))
|
||||
await ctx.channel.send(file = discord.File("gwendolyn/resources/wolf.png"))
|
||||
|
||||
os.remove("resources/wolf.png")
|
||||
os.remove("resources/wolfTemp.png")
|
||||
os.remove("gwendolyn/resources/wolf.png")
|
||||
os.remove("gwendolyn/resources/wolfTemp.png")
|
||||
else:
|
||||
self.bot.log("No returned data")
|
||||
await ctx.send("Could not find anything relating to your search")
|
||||
@@ -7,11 +7,11 @@ import lxml # Used in imageFunc
|
||||
import fandom # Used in findWikiPage
|
||||
import d20 # Used in rollDice
|
||||
import ast
|
||||
from .bedreNetflix import BedreNetflix
|
||||
from .nerdShit import NerdShit
|
||||
from .plex import Plex
|
||||
from .nerd_shit import NerdShit
|
||||
from .generators import Generators
|
||||
|
||||
from utils import cap
|
||||
from gwendolyn.utils import cap
|
||||
|
||||
fandom.set_lang("da")
|
||||
fandom.set_wiki("senkulpa")
|
||||
@@ -19,17 +19,17 @@ fandom.set_wiki("senkulpa")
|
||||
class MyStringifier(d20.MarkdownStringifier):
|
||||
def _str_expression(self, node):
|
||||
if node.comment == None:
|
||||
resultText = "Result"
|
||||
result_text = "Result"
|
||||
else:
|
||||
resultText = node.comment.capitalize()
|
||||
result_text = node.comment.capitalize()
|
||||
|
||||
return f"**{resultText}**: {self._stringify(node.roll)}\n**Total**: {int(node.total)}"
|
||||
return f"**{result_text}**: {self._stringify(node.roll)}\n**Total**: {int(node.total)}"
|
||||
|
||||
class Other():
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.bedreNetflix = BedreNetflix(self.bot)
|
||||
self.nerdShit = NerdShit(self.bot)
|
||||
self.plex = Plex(self.bot)
|
||||
self.nerd_shit = NerdShit(self.bot)
|
||||
self.generators = Generators(self.bot)
|
||||
|
||||
# Picks a random movie and returns information about it
|
||||
@@ -40,12 +40,12 @@ class Other():
|
||||
imdbClient = imdb.IMDb()
|
||||
|
||||
self.bot.log("Picking a movie")
|
||||
with open("resources/movies.txt", "r") as f:
|
||||
movieList = f.read().split("\n")
|
||||
movieName = random.choice(movieList)
|
||||
with open("gwendolyn/resources/movies.txt", "r") as f:
|
||||
movie_list = f.read().split("\n")
|
||||
movie_name = random.choice(movie_list)
|
||||
|
||||
self.bot.log(f"Searching for {movieName}")
|
||||
searchResult = imdbClient.search_movie(movieName)
|
||||
self.bot.log(f"Searching for {movie_name}")
|
||||
searchResult = imdbClient.search_movie(movie_name)
|
||||
|
||||
self.bot.log("Getting the data")
|
||||
movie = searchResult[0]
|
||||
@@ -74,17 +74,17 @@ class Other():
|
||||
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):
|
||||
sendMessage = "Good morning, "+str(author)
|
||||
send_message = "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):
|
||||
sendMessage = "Good afternoon, "+str(author)
|
||||
send_message = "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):
|
||||
sendMessage = "Good evening, "+str(author)
|
||||
send_message = "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):
|
||||
sendMessage = "Good night, "+str(author)
|
||||
send_message = "Good night, "+str(author)
|
||||
else:
|
||||
sendMessage = "Hello, "+str(author)
|
||||
send_message = "Hello, "+str(author)
|
||||
|
||||
await ctx.send(sendMessage)
|
||||
await ctx.send(send_message)
|
||||
|
||||
# Finds a random picture online
|
||||
async def imageFunc(self, ctx):
|
||||
@@ -183,13 +183,13 @@ class Other():
|
||||
|
||||
async def helpFunc(self, ctx, command):
|
||||
if command == "":
|
||||
with open("resources/help/help.txt",encoding="utf-8") as f:
|
||||
with open("gwendolyn/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:
|
||||
with open(f"gwendolyn/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)
|
||||
@@ -0,0 +1,536 @@
|
||||
"""Plex integration with the bot."""
|
||||
from math import floor, ceil
|
||||
import time
|
||||
import json
|
||||
import asyncio
|
||||
import requests
|
||||
import imdb
|
||||
import discord
|
||||
|
||||
class Plex():
|
||||
"""Container for Plex functions and commands."""
|
||||
def __init__(self,bot):
|
||||
self.bot = bot
|
||||
self.credentials = self.bot.credentials
|
||||
self.long_strings = self.bot.long_strings
|
||||
server_ip = ["localhost", "192.168.0.40"][self.bot.options["testing"]]
|
||||
|
||||
self.radarr_url = "http://"+server_ip+":7878/api/v3/"
|
||||
self.sonarr_url = "http://"+server_ip+":8989/api/"
|
||||
self.qbittorrent_url = "http://"+server_ip+":8080/api/v2/"
|
||||
self.movie_path = "/media/plex/Server/movies/"
|
||||
self.show_path = "/media/plex/Server/Shows/"
|
||||
|
||||
|
||||
async def request_movie(self, ctx, movie_name):
|
||||
"""Request a movie for the Plex Server"""
|
||||
await self.bot.defer(ctx)
|
||||
|
||||
self.bot.log("Searching for "+movie_name)
|
||||
movie_list = imdb.IMDb().search_movie(movie_name)
|
||||
movies = []
|
||||
for movie in movie_list:
|
||||
if movie["kind"] == "movie":
|
||||
movies.append(movie)
|
||||
if len(movies) > 5:
|
||||
movies = movies[:5]
|
||||
|
||||
if len(movies) == 1:
|
||||
message_title = "**Is it this movie?**"
|
||||
else:
|
||||
message_title = "**Is it any of these movies?**"
|
||||
|
||||
message_text = ""
|
||||
imdb_ids = []
|
||||
|
||||
for i, movie in enumerate(movies):
|
||||
try:
|
||||
message_text += "\n"+str(i+1)+") "+movie["title"]
|
||||
try:
|
||||
message_text += " ("+str(movie["year"])+")"
|
||||
except KeyError:
|
||||
self.bot.log(f"{movie['title']} has no year.")
|
||||
except KeyError:
|
||||
message_text += "Error"
|
||||
imdb_ids.append(movie.movieID)
|
||||
|
||||
self.bot.log(
|
||||
f"Returning a list of {len(movies)} possible movies: {imdb_ids}"
|
||||
)
|
||||
|
||||
embed = discord.Embed(
|
||||
title=message_title,
|
||||
description=message_text,
|
||||
colour=0x00FF00
|
||||
)
|
||||
|
||||
message = await ctx.send(embed=embed)
|
||||
|
||||
message_data = {"message_id":message.id,"imdb_ids":imdb_ids}
|
||||
|
||||
file_path = f"gwendolyn/resources/plex/old_message{ctx.channel.id}"
|
||||
with open(file_path,"w") as file_pointer:
|
||||
json.dump(message_data, file_pointer)
|
||||
|
||||
if len(movies) == 1:
|
||||
await message.add_reaction("✔️")
|
||||
else:
|
||||
for i in range(len(movies)):
|
||||
await message.add_reaction(["1️⃣","2️⃣","3️⃣","4️⃣","5️⃣"][i])
|
||||
|
||||
await message.add_reaction("❌")
|
||||
|
||||
message = await ctx.channel.fetch_message(message.id)
|
||||
if (message.content != "" and
|
||||
not isinstance(ctx.channel, discord.DMChannel)):
|
||||
await message.clear_reactions()
|
||||
|
||||
|
||||
async def add_movie(self, message, imdb_id, edit_message = True):
|
||||
"""Add a movie to Plex server."""
|
||||
if imdb_id is None:
|
||||
self.bot.log("Did not find what the user was searching for")
|
||||
if edit_message:
|
||||
await message.edit(
|
||||
embed = None,
|
||||
content = "Try searching for the IMDB id"
|
||||
)
|
||||
else:
|
||||
await message.channel.send("Try searching for the IMDB id")
|
||||
else:
|
||||
self.bot.log("Trying to add movie "+str(imdb_id))
|
||||
api_key = self.credentials["radarr_key"]
|
||||
request_url = self.radarr_url+"movie/lookup/imdb?imdbId=tt"+imdb_id
|
||||
request_url += "&apiKey="+api_key
|
||||
response = requests.get(request_url)
|
||||
lookup_data = response.json()
|
||||
post_data = {"qualityProfileId": 1,
|
||||
"rootFolder_path" : self.movie_path,
|
||||
"monitored" : True,
|
||||
"addOptions": {"searchForMovie": True}}
|
||||
for key in ["tmdbId","title","titleSlug","images","year"]:
|
||||
post_data.update({key : lookup_data[key]})
|
||||
|
||||
response = requests.post(
|
||||
url= self.radarr_url+"movie?apikey="+api_key,
|
||||
json = post_data
|
||||
)
|
||||
|
||||
if response.status_code == 201:
|
||||
success_message = "{} successfully added to Plex".format(
|
||||
post_data["title"]
|
||||
)
|
||||
if edit_message:
|
||||
await message.edit(
|
||||
embed = None,
|
||||
content = success_message
|
||||
)
|
||||
else:
|
||||
await message.channel.send(success_message)
|
||||
|
||||
self.bot.log("Added "+post_data["title"]+" to Plex")
|
||||
elif response.status_code == 400:
|
||||
fail_text = self.long_strings["Already on Plex"].format(
|
||||
post_data['title']
|
||||
)
|
||||
if edit_message:
|
||||
await message.edit(embed = None, content = fail_text)
|
||||
else:
|
||||
await message.channel.send(fail_text)
|
||||
else:
|
||||
if edit_message:
|
||||
await message.edit(
|
||||
embed = None,
|
||||
content = "Something went wrong"
|
||||
)
|
||||
else:
|
||||
await message.channel.send("Something went wrong")
|
||||
self.bot.log(str(response.status_code)+" "+response.reason)
|
||||
|
||||
async def request_show(self, ctx, show_name):
|
||||
"""Request a show for the Plex server."""
|
||||
await self.bot.defer(ctx)
|
||||
|
||||
self.bot.log("Searching for "+show_name)
|
||||
movies = imdb.IMDb().search_movie(show_name) # Replace with tvdb
|
||||
shows = []
|
||||
for movie in movies:
|
||||
if movie["kind"] in ["tv series","tv miniseries"]:
|
||||
shows.append(movie)
|
||||
if len(shows) > 5:
|
||||
shows = shows[:5]
|
||||
|
||||
if len(shows) == 1:
|
||||
message_title = "**Is it this show?**"
|
||||
else:
|
||||
message_title = "**Is it any of these shows?**"
|
||||
|
||||
message_text = ""
|
||||
imdb_names = []
|
||||
|
||||
for i, show in enumerate(shows):
|
||||
try:
|
||||
message_text += f"\n{i+1}) {show['title']} ({show['year']})"
|
||||
except KeyError:
|
||||
try:
|
||||
message_text += "\n"+str(i+1)+") "+show["title"]
|
||||
except KeyError:
|
||||
message_text += "Error"
|
||||
imdb_names.append(show["title"])
|
||||
|
||||
self.bot.log(
|
||||
f"Returning a list of {len(shows)} possible shows: {imdb_names}"
|
||||
)
|
||||
|
||||
embed = discord.Embed(
|
||||
title=message_title,
|
||||
description=message_text,
|
||||
colour=0x00FF00
|
||||
)
|
||||
|
||||
message = await ctx.send(embed=embed)
|
||||
|
||||
message_data = {"message_id":message.id,"imdb_names":imdb_names}
|
||||
|
||||
file_path = "gwendolyn/resources/plex/old_message"+str(ctx.channel.id)
|
||||
with open(file_path,"w") as file_pointer:
|
||||
json.dump(message_data, file_pointer)
|
||||
|
||||
if len(shows) == 1:
|
||||
await message.add_reaction("✔️")
|
||||
else:
|
||||
for i in range(len(shows)):
|
||||
await message.add_reaction(["1️⃣","2️⃣","3️⃣","4️⃣","5️⃣"][i])
|
||||
|
||||
await message.add_reaction("❌")
|
||||
|
||||
message = await ctx.channel.fetch_message(message.id)
|
||||
if message.content != "":
|
||||
if not isinstance(ctx.channel, discord.DMChannel):
|
||||
await message.clear_reactions()
|
||||
|
||||
async def add_show(self, message, imdb_name):
|
||||
"""Add the requested show to Plex."""
|
||||
if imdb_name is None:
|
||||
self.bot.log("Did not find what the user was searching for")
|
||||
await message.edit(
|
||||
embed = None,
|
||||
content = "Try searching for the IMDB id"
|
||||
)
|
||||
else:
|
||||
self.bot.log("Trying to add show "+str(imdb_name))
|
||||
api_key = self.credentials["sonarr_key"]
|
||||
request_url = self.sonarr_url+"series/lookup?term="
|
||||
request_url += imdb_name.replace(" ","%20")
|
||||
request_url += "&apiKey="+api_key
|
||||
response = requests.get(request_url)
|
||||
lookup_data = response.json()[0]
|
||||
post_data = {
|
||||
"ProfileId" : 1,
|
||||
"rootFolder_path" : self.show_path,
|
||||
"monitored" : True,
|
||||
"addOptions" : {"searchForMissingEpisodes" : True}
|
||||
}
|
||||
for key in ["tvdbId","title","titleSlug","images","seasons"]:
|
||||
post_data.update({key : lookup_data[key]})
|
||||
|
||||
|
||||
response = requests.post(
|
||||
url= self.sonarr_url+"series?apikey="+api_key,
|
||||
json = post_data
|
||||
)
|
||||
|
||||
if response.status_code == 201:
|
||||
await message.edit(
|
||||
embed = None,
|
||||
content = post_data["title"]+" successfully added to Plex"
|
||||
)
|
||||
self.bot.log("Added a "+post_data["title"]+" to Plex")
|
||||
elif response.status_code == 400:
|
||||
text = self.long_strings["Already on Plex"].format(
|
||||
post_data['title']
|
||||
)
|
||||
await message.edit(embed = None, content = text)
|
||||
else:
|
||||
await message.edit(
|
||||
embed = None,
|
||||
content = "Something went wrong"
|
||||
)
|
||||
self.bot.log(str(response.status_code)+" "+response.reason)
|
||||
|
||||
async def __generate_download_list(self, show_dm, show_movies, show_shows,
|
||||
episodes):
|
||||
"""Generate a list of all torrents.
|
||||
|
||||
*Returns*
|
||||
message_text: str
|
||||
A formatted list of all torrents
|
||||
|
||||
all_downloaded: bool
|
||||
Whether all torrents are downloaded
|
||||
"""
|
||||
self.bot.log("Generating torrent list")
|
||||
title_width = 100
|
||||
message = []
|
||||
all_downloaded = True
|
||||
|
||||
if show_dm:
|
||||
message.append("")
|
||||
dm_section_title = "*Torrent Downloads*"
|
||||
dm_section_title_line = "-"*((title_width-len(dm_section_title))//2)
|
||||
message.append(
|
||||
dm_section_title_line+dm_section_title+dm_section_title_line
|
||||
)
|
||||
response = requests.get(self.qbittorrent_url+"torrents/info")
|
||||
torrent_list = response.json()
|
||||
|
||||
if len(torrent_list) > 0:
|
||||
for torrent in torrent_list:
|
||||
torrent_name = torrent["name"]
|
||||
if len(torrent_name) > 30:
|
||||
if torrent_name[26] == " ":
|
||||
torrent_name = torrent_name[:26]+"...."
|
||||
else:
|
||||
torrent_name = torrent_name[:27]+"..."
|
||||
while len(torrent_name) < 30:
|
||||
torrent_name += " "
|
||||
|
||||
if torrent["size"] == 0:
|
||||
download_ratio = 0
|
||||
elif torrent["amount_left"] == 0:
|
||||
download_ratio = 1
|
||||
else:
|
||||
download_ratio = min(
|
||||
torrent["downloaded"]/torrent["size"],
|
||||
1
|
||||
)
|
||||
progress_bar = "|"+("█"*floor(download_ratio*20))
|
||||
while len(progress_bar) < 21:
|
||||
progress_bar += " "
|
||||
|
||||
progress_bar += "| "+str(floor(download_ratio*100))+"%"
|
||||
|
||||
while len(progress_bar) < 27:
|
||||
progress_bar += " "
|
||||
|
||||
eta_in_seconds = torrent["eta"]
|
||||
|
||||
if eta_in_seconds >= 8640000:
|
||||
eta = "∞"
|
||||
else:
|
||||
eta = ""
|
||||
if eta_in_seconds >= 86400:
|
||||
eta += str(floor(eta_in_seconds/86400))+"d "
|
||||
if eta_in_seconds >= 3600:
|
||||
eta += str(floor((eta_in_seconds%86400)/3600))+"h "
|
||||
if eta_in_seconds >= 60:
|
||||
eta += str(floor((eta_in_seconds%3600)/60))+"m "
|
||||
|
||||
eta += str(eta_in_seconds%60)+"s"
|
||||
|
||||
torrent_info = f"{torrent_name} {progress_bar} "
|
||||
torrent_info += f"(Eta: {eta})"
|
||||
|
||||
if torrent["state"] == "stalledDL":
|
||||
torrent_info += " (Stalled)"
|
||||
|
||||
if not (download_ratio == 1 and
|
||||
torrent["last_activity"] < time.time()-7200):
|
||||
message.append(torrent_info)
|
||||
|
||||
if download_ratio < 1 and torrent["state"] != "stalledDL":
|
||||
all_downloaded = False
|
||||
else:
|
||||
message.append("No torrents currently downloading")
|
||||
|
||||
if show_movies:
|
||||
message.append("")
|
||||
movies_section_title = "*Missing movies not downloading*"
|
||||
movies_section_line = (
|
||||
"-"*((title_width-len(movies_section_title))//2)
|
||||
)
|
||||
message.append(
|
||||
movies_section_line+movies_section_title+movies_section_line
|
||||
)
|
||||
movie_list = requests.get(
|
||||
self.radarr_url+"movie?api_key="+self.credentials["radarr_key"]
|
||||
).json()
|
||||
movie_queue = requests.get(
|
||||
self.radarr_url+"queue?api_key="+self.credentials["radarr_key"]
|
||||
).json()
|
||||
movie_queue_ids = []
|
||||
|
||||
for queue_item in movie_queue["records"]:
|
||||
movie_queue_ids.append(queue_item["movieId"])
|
||||
|
||||
for movie in movie_list:
|
||||
if (not movie["hasFile"] and
|
||||
movie["id"] not in movie_queue_ids):
|
||||
movie_name = movie["title"]
|
||||
if len(movie_name) > 40:
|
||||
if movie_name[36] == " ":
|
||||
movie_name = movie_name[:36]+"...."
|
||||
else:
|
||||
movie_name = movie_name[:37]+"..."
|
||||
|
||||
while len(movie_name) < 41:
|
||||
movie_name += " "
|
||||
|
||||
if movie["monitored"]:
|
||||
movie_info = movie_name+"Could not find a torrent"
|
||||
else:
|
||||
movie_info = self.long_strings["No torrent"].format(
|
||||
movie_name
|
||||
)
|
||||
|
||||
message.append(movie_info)
|
||||
|
||||
if show_shows:
|
||||
message.append("")
|
||||
show_section_title = "*Missing shows not downloading*"
|
||||
show_section_line = "-"*((title_width-len(show_section_title))//2)
|
||||
message.append(
|
||||
show_section_line+show_section_title+show_section_line
|
||||
)
|
||||
|
||||
show_list = requests.get(
|
||||
self.sonarr_url+"series?api_key="+self.credentials["sonarr_key"]
|
||||
).json()
|
||||
|
||||
for show in show_list:
|
||||
if show["seasons"][0]["seasonNumber"] == 0:
|
||||
seasons = show["seasons"][1:]
|
||||
else:
|
||||
seasons = show["seasons"]
|
||||
if any(
|
||||
(
|
||||
i["statistics"]["episodeCount"] !=
|
||||
i["statistics"]["totalEpisodeCount"]
|
||||
) for i in seasons):
|
||||
if all(
|
||||
i["statistics"]["episodeCount"] == 0 for i in seasons
|
||||
):
|
||||
message.append(show["title"] + " (all episodes)")
|
||||
else:
|
||||
if episodes:
|
||||
missing_episodes = sum(
|
||||
(i["statistics"]["totalEpisodeCount"] -
|
||||
i["statistics"]["episodeCount"])
|
||||
for i in seasons)
|
||||
message.append(
|
||||
f"{show['title']} ({missing_episodes} episodes)"
|
||||
)
|
||||
|
||||
message.append("-"*title_width)
|
||||
|
||||
message_text = "```"+"\n".join(message[1:])+"```"
|
||||
if message_text == "``````":
|
||||
message_text = self.long_strings["No torrents downloading"]
|
||||
return message_text, all_downloaded
|
||||
|
||||
async def downloading(self, ctx, content):
|
||||
"""Send message with list of all downloading torrents."""
|
||||
async def send_long_message(ctx,message_text):
|
||||
if len(message_text) <= 1994:
|
||||
await ctx.send("```"+message_text+"```")
|
||||
else:
|
||||
cut_off_index = message_text[:1994].rfind("\n")
|
||||
await ctx.send("```"+message_text[:cut_off_index]+"```")
|
||||
await send_long_message(ctx,message_text[cut_off_index+1:])
|
||||
|
||||
await self.bot.defer(ctx)
|
||||
|
||||
# showDM, showMovies, showShows, episodes
|
||||
parameters = [False, False, False, False]
|
||||
show_dm_args = ["d", "dm", "downloading", "downloadmanager"]
|
||||
show_movies_args = ["m", "movies"]
|
||||
show_shows_args = ["s", "shows", "series"]
|
||||
show_episode_args = ["e", "episodes"]
|
||||
arg_list = [
|
||||
show_dm_args, show_movies_args, show_shows_args, show_episode_args
|
||||
]
|
||||
input_args = []
|
||||
valid_arguments = True
|
||||
|
||||
while content != "" and valid_arguments:
|
||||
if content[0] == " ":
|
||||
content = content[1:]
|
||||
elif content[0] == "-":
|
||||
if content[1] == "-":
|
||||
arg_start = 2
|
||||
if " " in content:
|
||||
arg_stop = content.find(" ")
|
||||
else:
|
||||
arg_stop = None
|
||||
else:
|
||||
arg_start = 1
|
||||
arg_stop = 2
|
||||
|
||||
input_args.append(content[arg_start:arg_stop])
|
||||
if arg_stop is None:
|
||||
content = ""
|
||||
else:
|
||||
content = content[arg_stop:]
|
||||
else:
|
||||
valid_arguments = False
|
||||
|
||||
if valid_arguments:
|
||||
for arg_index, arg_aliases in enumerate(arg_list):
|
||||
arg_in_input = [i in input_args for i in arg_aliases]
|
||||
if any(arg_in_input):
|
||||
input_args.remove(arg_aliases[arg_in_input.index(True)])
|
||||
parameters[arg_index] = True
|
||||
|
||||
if len(input_args) != 0 or (not parameters[2] and parameters[3]):
|
||||
valid_arguments = False
|
||||
|
||||
show_anything = any(i for i in parameters)
|
||||
if not (valid_arguments and show_anything):
|
||||
await ctx.send(self.long_strings["Invalid parameters"])
|
||||
else:
|
||||
message_text, all_downloaded = await self.__generate_download_list(
|
||||
*parameters
|
||||
)
|
||||
if not message_text.startswith("```"):
|
||||
await ctx.send(message_text)
|
||||
|
||||
elif len(message_text) > 2000:
|
||||
message_text = message_text[3:-3]
|
||||
await send_long_message(ctx,message_text)
|
||||
|
||||
elif all_downloaded:
|
||||
await ctx.send(message_text)
|
||||
|
||||
else:
|
||||
updates_left = 60
|
||||
message_text = self.long_strings["Update"].format(
|
||||
message_text[:-3], ceil(updates_left/6)
|
||||
)
|
||||
old_message = await ctx.send(message_text)
|
||||
|
||||
while ((not all_downloaded) and updates_left > 0):
|
||||
await asyncio.sleep(10)
|
||||
updates_left -= 1
|
||||
message_text, all_downloaded = await (
|
||||
self.__generate_download_list(*parameters)
|
||||
)
|
||||
message_text = self.long_strings["Update"].format(
|
||||
message_text[:-3],
|
||||
ceil(updates_left/6)
|
||||
)
|
||||
await old_message.edit(content = message_text)
|
||||
|
||||
message_text, all_downloaded = await (
|
||||
self.__generate_download_list(*parameters)
|
||||
)
|
||||
|
||||
if message_text.startswith("```"):
|
||||
if all_downloaded:
|
||||
self.bot.log("All torrents are downloaded")
|
||||
else:
|
||||
message_text = self.long_strings["No updates"].format(
|
||||
message_text[:-3]
|
||||
)
|
||||
self.bot.log("The message updated 20 times")
|
||||
|
||||
await old_message.edit(content = message_text)
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
__all__ = ["StarWars"]
|
||||
|
||||
from .starWars import StarWars
|
||||
from .star_wars import StarWars
|
||||
@@ -1,6 +1,6 @@
|
||||
from .starWarsChar import StarWarsChar
|
||||
from .starWarsRoll import StarWarsRoll
|
||||
from .starWarsDestiny import StarWarsDestiny
|
||||
from .star_wars_char import StarWarsChar
|
||||
from .star_wars_roll import StarWarsRoll
|
||||
from .star_wars_destiny import StarWarsDestiny
|
||||
|
||||
class StarWars():
|
||||
def __init__(self, bot):
|
||||
+13
-13
@@ -6,16 +6,16 @@ class StarWarsChar():
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
||||
def getCharName(self, user : str):
|
||||
self.bot.log("Getting name for "+self.bot.databaseFuncs.getName(user)+"'s character")
|
||||
def getChar_name(self, user : str):
|
||||
self.bot.log("Getting name for "+self.bot.database_funcs.get_name(user)+"'s character")
|
||||
userCharacter = self.bot.database["starwars characters"].find_one({"_id":user})
|
||||
|
||||
if userCharacter != None:
|
||||
self.bot.log("Name is "+userCharacter["Name"])
|
||||
return userCharacter["Name"]
|
||||
else:
|
||||
self.bot.log("Just using "+self.bot.databaseFuncs.getName(user))
|
||||
return self.bot.databaseFuncs.getName(user)
|
||||
self.bot.log("Just using "+self.bot.database_funcs.get_name(user))
|
||||
return self.bot.database_funcs.get_name(user)
|
||||
|
||||
def setUpDict(self, cmd : dict):
|
||||
self.bot.log("Setting up a dictionary in a nice way")
|
||||
@@ -239,7 +239,7 @@ class StarWarsChar():
|
||||
|
||||
return name, text1+text2+"\n\n"+text3+divider+text4+"\n"+divider+text5+text6+text7+text8
|
||||
|
||||
def charData(self,user : str,cmd : str):
|
||||
def char_data(self,user : str,cmd : str):
|
||||
userCharacter = self.bot.database["starwars characters"].find_one({"_id":user})
|
||||
|
||||
key = string.capwords(cmd.split(" ")[0])
|
||||
@@ -252,7 +252,7 @@ class StarWarsChar():
|
||||
if cmd == "":
|
||||
break
|
||||
|
||||
self.bot.log("Looking for "+self.bot.databaseFuncs.getName(user)+"'s character")
|
||||
self.bot.log("Looking for "+self.bot.database_funcs.get_name(user)+"'s character")
|
||||
if userCharacter != None:
|
||||
self.bot.log("Found it! Looking for "+key+" in the data")
|
||||
if key in userCharacter:
|
||||
@@ -303,7 +303,7 @@ class StarWarsChar():
|
||||
return cmd[0]+" added to "+key+" for " + userCharacter["Name"]
|
||||
|
||||
elif key == "Weapons":
|
||||
with open("resources/starWars/starwarstemplates.json", "r") as f:
|
||||
with open("gwendolyn/resources/star_wars/starwarstemplates.json", "r") as f:
|
||||
templates = json.load(f)
|
||||
newWeapon = templates["Weapon"]
|
||||
self.bot.log("Adding "+cmd+" to "+key)
|
||||
@@ -495,20 +495,20 @@ class StarWarsChar():
|
||||
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:
|
||||
self.bot.log("Makin' a character for "+self.bot.database_funcs.get_name(user))
|
||||
with open("gwendolyn/resources/star_wars/starwarstemplates.json", "r") as f:
|
||||
templates = json.load(f)
|
||||
newChar = templates["Character"]
|
||||
newChar["_id"] = user
|
||||
self.bot.database["starwars characters"].insert_one(newChar)
|
||||
await ctx.send("Character for " + self.bot.databaseFuncs.getName(user) + " created")
|
||||
await ctx.send("Character for " + self.bot.database_funcs.get_name(user) + " created")
|
||||
else:
|
||||
if cmd == "Purge":
|
||||
self.bot.log("Deleting "+self.bot.databaseFuncs.getName(user)+"'s character")
|
||||
self.bot.log("Deleting "+self.bot.database_funcs.get_name(user)+"'s character")
|
||||
self.bot.database["starwars characters"].delete_one({"_id":user})
|
||||
await ctx.send("Character for " + self.bot.databaseFuncs.getName(user) + " deleted")
|
||||
await ctx.send("Character for " + self.bot.database_funcs.get_name(user) + " deleted")
|
||||
else:
|
||||
await ctx.send(self.replaceWithSpaces(str(self.charData(user,cmd))))
|
||||
await ctx.send(self.replaceWithSpaces(str(self.char_data(user,cmd))))
|
||||
|
||||
if returnEmbed:
|
||||
em = discord.Embed(title = title, description = text, colour=0xDEADBF)
|
||||
+18
-18
@@ -4,16 +4,16 @@ class StarWarsDestiny():
|
||||
|
||||
def destinyNew(self, num : int):
|
||||
self.bot.log("Creating a new destiny pool with "+str(num)+" players")
|
||||
roll, diceResults = self.bot.starWars.roll.roll(0,0,0,0,0,0,num)
|
||||
roll, diceResults = self.bot.star_wars.roll.roll(0,0,0,0,0,0,num)
|
||||
roll = "".join(sorted(roll))
|
||||
|
||||
with open("resources/starWars/destinyPoints.txt","wt") as f:
|
||||
with open("gwendolyn/resources/star_wars/destinyPoints.txt","wt") as f:
|
||||
f.write(roll)
|
||||
|
||||
return "Rolled for Destiny Points and got:\n"+self.bot.starWars.roll.diceResultToEmoji(diceResults)+"\n"+self.bot.starWars.roll.resultToEmoji(roll)
|
||||
return "Rolled for Destiny Points and got:\n"+self.bot.star_wars.roll.diceResultToEmoji(diceResults)+"\n"+self.bot.star_wars.roll.resultToEmoji(roll)
|
||||
|
||||
def destinyUse(self, user : str):
|
||||
with open("resources/starWars/destinyPoints.txt","rt") as f:
|
||||
with open("gwendolyn/resources/star_wars/destinyPoints.txt","rt") as f:
|
||||
points = f.read()
|
||||
|
||||
if user == "Nikolaj":
|
||||
@@ -21,10 +21,10 @@ class StarWarsDestiny():
|
||||
if 'B' in points:
|
||||
points = points.replace("B","L",1)
|
||||
points = "".join(sorted(points))
|
||||
with open("resources/starWars/destinyPoints.txt","wt") as f:
|
||||
with open("gwendolyn/resources/star_wars/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.starWars.roll.resultToEmoji(points)
|
||||
return "Used a dark side destiny point. Destiny pool is now:\n"+self.bot.star_wars.roll.resultToEmoji(points)
|
||||
else:
|
||||
self.bot.log("There were no dark side destiny points")
|
||||
return "No dark side destiny points"
|
||||
@@ -33,10 +33,10 @@ class StarWarsDestiny():
|
||||
if 'L' in points:
|
||||
points = points.replace("L","B",1)
|
||||
points = "".join(sorted(points))
|
||||
with open("resources/starWars/destinyPoints.txt","wt") as f:
|
||||
with open("gwendolyn/resources/star_wars/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.starWars.roll.resultToEmoji(points)
|
||||
return "Used a light side destiny point. Destiny pool is now:\n"+self.bot.star_wars.roll.resultToEmoji(points)
|
||||
else:
|
||||
self.bot.log("There were no dark side destiny points")
|
||||
return "No light side destiny points"
|
||||
@@ -51,22 +51,22 @@ class StarWarsDestiny():
|
||||
|
||||
if cmd == "":
|
||||
self.bot.log("Retrieving destiny pool info")
|
||||
with open("resources/starWars/destinyPoints.txt","rt") as f:
|
||||
sendMessage = self.bot.starWars.roll.resultToEmoji(f.read())
|
||||
with open("gwendolyn/resources/star_wars/destinyPoints.txt","rt") as f:
|
||||
send_message = self.bot.star_wars.roll.resultToEmoji(f.read())
|
||||
else:
|
||||
commands = cmd.upper().split(" ")
|
||||
if commands[0] == "N":
|
||||
if len(commands) > 1:
|
||||
sendMessage = self.destinyNew(int(commands[1]))
|
||||
send_message = self.destinyNew(int(commands[1]))
|
||||
else:
|
||||
sendMessage = "You need to give an amount of players (error code 921)"
|
||||
send_message = "You need to give an amount of players (error code 921)"
|
||||
elif commands[0] == "U":
|
||||
sendMessage = self.destinyUse(user)
|
||||
send_message = self.destinyUse(user)
|
||||
else:
|
||||
sendMessage = "I didn't quite understand that (error code 922)"
|
||||
send_message = "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:]:
|
||||
message_list = send_message.split("\n")
|
||||
await ctx.send(message_list[0])
|
||||
if len(message_list) > 1:
|
||||
for messageItem in message_list[1:]:
|
||||
await ctx.channel.send(messageItem)
|
||||
+24
-24
@@ -3,8 +3,8 @@ import re
|
||||
import string
|
||||
import json
|
||||
|
||||
with open("resources/starWars/starwarsskills.json", "r") as f:
|
||||
skillData = json.load(f)
|
||||
with open("gwendolyn/resources/star_wars/starwarsskills.json", "r") as f:
|
||||
skill_data = json.load(f)
|
||||
|
||||
class StarWarsRoll():
|
||||
def __init__(self, bot):
|
||||
@@ -288,12 +288,12 @@ 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
|
||||
|
||||
sendMessage = "Roll: "+str(roll)+"\nInjury:\n"+results
|
||||
send_message = "Roll: "+str(roll)+"\nInjury:\n"+results
|
||||
|
||||
messageList = sendMessage.split("\n")
|
||||
await ctx.send(messageList[0])
|
||||
if len(messageList) > 1:
|
||||
for messageItem in messageList[1:]:
|
||||
message_list = send_message.split("\n")
|
||||
await ctx.send(message_list[0])
|
||||
if len(message_list) > 1:
|
||||
for messageItem in message_list[1:]:
|
||||
await ctx.channel.send(messageItem)
|
||||
|
||||
# Parses the command into something the other functions understand
|
||||
@@ -302,7 +302,7 @@ class StarWarsRoll():
|
||||
cmd = re.sub(' +',' ',cmd.upper()) + " "
|
||||
if cmd[0] == " ":
|
||||
cmd = cmd[1:]
|
||||
cmd = self.bot.starWars.character.replaceSpaces(string.capwords(cmd))
|
||||
cmd = self.bot.star_wars.character.replaceSpaces(string.capwords(cmd))
|
||||
commands = cmd.split(" ")
|
||||
validCommand = False
|
||||
|
||||
@@ -312,19 +312,19 @@ class StarWarsRoll():
|
||||
rollParameters = [0,0,0,0,0,0,0]
|
||||
|
||||
if string.capwords(commands[0]) == "Obligations":
|
||||
sendMessage = self.obligationRoll()
|
||||
send_message = self.obligationRoll()
|
||||
|
||||
elif string.capwords(commands[0]) in skillData:
|
||||
elif string.capwords(commands[0]) in skill_data:
|
||||
self.bot.log("Oh look! This guy has skills!")
|
||||
if self.bot.starWars.character.userHasChar(user):
|
||||
if self.bot.star_wars.character.userHasChar(user):
|
||||
self.bot.log("They have a character. That much we know")
|
||||
skillLevel = self.bot.starWars.character.charData(user,"Skills " + string.capwords(commands[0]))
|
||||
skillLevel = self.bot.star_wars.character.char_data(user,"Skills " + string.capwords(commands[0]))
|
||||
|
||||
if string.capwords(commands[0]) == "Lightsaber":
|
||||
self.bot.log("The skill is lightsaber")
|
||||
charLevel = self.bot.starWars.character.charData(user,"Characteristics " + self.bot.starWars.character.lightsaberChar(user))
|
||||
charLevel = self.bot.star_wars.character.char_data(user,"Characteristics " + self.bot.star_wars.character.lightsaberChar(user))
|
||||
else:
|
||||
charLevel = self.bot.starWars.character.charData(user,"Characteristics " + skillData[string.capwords(commands[0])])
|
||||
charLevel = self.bot.star_wars.character.char_data(user,"Characteristics " + skill_data[string.capwords(commands[0])])
|
||||
|
||||
abilityDice = abs(charLevel-skillLevel)
|
||||
proficiencyDice = min(skillLevel,charLevel)
|
||||
@@ -334,14 +334,14 @@ class StarWarsRoll():
|
||||
validCommand = True
|
||||
else:
|
||||
self.bot.log("Okay, no they don't i guess")
|
||||
sendMessage = "You don't have a user. You can make one with /starwarscharacter"
|
||||
send_message = "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":
|
||||
sendMessage = "Did you mean \"Ranged - Heavy\" or \"Ranged - Light\" (error code 913)"
|
||||
send_message = "Did you mean \"Ranged - Heavy\" or \"Ranged - Light\" (error code 913)"
|
||||
else:
|
||||
sendMessage = "Did you mean \"Piloting - Planetary\" or \"Piloting - Space\" (error code 913)"
|
||||
send_message = "Did you mean \"Piloting - Planetary\" or \"Piloting - Space\" (error code 913)"
|
||||
else:
|
||||
validCommand = True
|
||||
|
||||
@@ -372,19 +372,19 @@ class StarWarsRoll():
|
||||
|
||||
simplified = self.simplify(rollResults)
|
||||
|
||||
name = self.bot.starWars.character.getCharName(user)
|
||||
name = self.bot.star_wars.character.getChar_name(user)
|
||||
|
||||
self.bot.log("Returns results and simplified results")
|
||||
|
||||
if simplified == "":
|
||||
sendMessage = name + " rolls: " + "\n" + self.diceResultToEmoji(diceResults) + "\nEverything cancels out!"
|
||||
send_message = name + " rolls: " + "\n" + self.diceResultToEmoji(diceResults) + "\nEverything cancels out!"
|
||||
else:
|
||||
sendMessage = name + " rolls: " + "\n" + self.diceResultToEmoji(diceResults) + "\n" + self.resultToEmoji(simplified)
|
||||
send_message = 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:]:
|
||||
message_list = send_message.split("\n")
|
||||
await ctx.send(message_list[0])
|
||||
if len(message_list) > 1:
|
||||
for messageItem in message_list[1:]:
|
||||
if messageItem == "":
|
||||
self.bot.log("Tried to send empty message")
|
||||
else:
|
||||
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
Contains the Gwendolyn class, a subclass of the discord command bot.
|
||||
|
||||
*Classes*
|
||||
---------
|
||||
Gwendolyn(discord.ext.commands.Bot)
|
||||
"""
|
||||
import os # Used for loading cogs in Gwendolyn.addCogs
|
||||
import finnhub # Used to add a finhub client to the bot
|
||||
import discord # Used for discord.Intents and discord.Status
|
||||
import discord_slash # Used to initialized SlashCommands object
|
||||
import git # Used to pull when stopping
|
||||
|
||||
from discord.ext import commands # Used to inherit from commands.bot
|
||||
from pymongo import MongoClient # Used for database management
|
||||
from gwendolyn.funcs import Money, StarWars, Games, Other, LookupFuncs
|
||||
from gwendolyn.utils import (get_options, get_credentials, log_this,
|
||||
DatabaseFuncs, EventHandler, ErrorHandler,
|
||||
long_strings)
|
||||
|
||||
|
||||
class Gwendolyn(commands.Bot):
|
||||
"""
|
||||
A multifunctional Discord bot.
|
||||
|
||||
*Methods*
|
||||
---------
|
||||
log(messages: Union[str, list], channel: str = "",
|
||||
level: int = 20)
|
||||
stop(ctx: discord_slash.context.SlashContext)
|
||||
defer(ctx: discord_slash.context.SlashContext)
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the bot."""
|
||||
intents = discord.Intents.default()
|
||||
intents.members = True
|
||||
initiation_parameters = {
|
||||
"command_prefix": " ",
|
||||
"case_insensitive": True,
|
||||
"intents": intents,
|
||||
"status": discord.Status.dnd
|
||||
}
|
||||
super().__init__(**initiation_parameters)
|
||||
|
||||
self._add_clients_and_options()
|
||||
self._add_util_classes()
|
||||
self._add_function_containers()
|
||||
self._add_cogs()
|
||||
|
||||
def _add_clients_and_options(self):
|
||||
"""Add all the client, option and credentials objects."""
|
||||
self.long_strings = long_strings()
|
||||
self.options = get_options()
|
||||
self.credentials = get_credentials()
|
||||
finnhub_key = self.credentials["finnhub_key"]
|
||||
self.finnhub_client = finnhub.Client(api_key=finnhub_key)
|
||||
mongo_user = self.credentials["mongo_db_user"]
|
||||
mongo_password = self.credentials["mongo_db_password"]
|
||||
mongo_url = f"mongodb+srv://{mongo_user}:{mongo_password}@gwendolyn"
|
||||
mongo_url += ".qkwfy.mongodb.net/Gwendolyn?retryWrites=true&w=majority"
|
||||
database_clint = MongoClient(mongo_url)
|
||||
|
||||
if self.options["testing"]:
|
||||
self.log("Testing mode")
|
||||
self.database = database_clint["Gwendolyn-Test"]
|
||||
else:
|
||||
self.database = database_clint["Gwendolyn"]
|
||||
|
||||
def _add_util_classes(self):
|
||||
"""Add all the classes used as utility."""
|
||||
self.database_funcs = DatabaseFuncs(self)
|
||||
self.event_handler = EventHandler(self)
|
||||
self.error_handler = ErrorHandler(self)
|
||||
slash_parameters = {
|
||||
"sync_commands": True,
|
||||
"sync_on_cog_reload": True,
|
||||
"override_type": True
|
||||
}
|
||||
self.slash = discord_slash.SlashCommand(self, **slash_parameters)
|
||||
|
||||
def _add_function_containers(self):
|
||||
"""Add all the function containers used for commands."""
|
||||
self.star_wars = StarWars(self)
|
||||
self.other = Other(self)
|
||||
self.lookup_funcs = LookupFuncs(self)
|
||||
self.games = Games(self)
|
||||
self.money = Money(self)
|
||||
|
||||
def _add_cogs(self):
|
||||
"""Load cogs."""
|
||||
for filename in os.listdir("./gwendolyn/cogs"):
|
||||
if filename.endswith(".py"):
|
||||
self.load_extension(f"gwendolyn.cogs.{filename[:-3]}")
|
||||
|
||||
def log(self, messages, channel: str = "", level: int = 20):
|
||||
"""Log a message. Described in utils/util_functions.py."""
|
||||
log_this(messages, channel, level)
|
||||
|
||||
async def stop(self, ctx: discord_slash.context.SlashContext):
|
||||
"""
|
||||
Stop the bot, and stop running games.
|
||||
|
||||
Only stops the bot if the user in ctx.author is one of the
|
||||
admins given in options.txt.
|
||||
|
||||
*parameters*
|
||||
------------
|
||||
ctx: discord_slash.context.SlashContext
|
||||
The context of the "/stop" slash command.
|
||||
"""
|
||||
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.database_funcs.wipe_games()
|
||||
if not self.options["testing"]:
|
||||
git_client = git.cmd.Git("")
|
||||
git_client.pull()
|
||||
|
||||
self.log("Logging out", level=25)
|
||||
await self.close()
|
||||
else:
|
||||
log_message = f"{ctx.author.display_name} tried to stop me!"
|
||||
self.log(log_message, str(ctx.channel_id))
|
||||
await ctx.send(f"I don't think I will, {ctx.author.display_name}")
|
||||
|
||||
async def defer(self, ctx: discord_slash.context.SlashContext):
|
||||
"""Send a "Gwendolyn is thinking" message to the user."""
|
||||
try:
|
||||
await ctx.defer()
|
||||
except discord_slash.error.AlreadyResponded:
|
||||
self.log("defer failed")
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 153 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user