Changed directory before importing config file, to stop it from erroring out when looking for required files
This commit is contained in:
parent
556022abbc
commit
8919b1210c
2
bot.py
2
bot.py
|
@ -6,6 +6,7 @@ import logging
|
|||
import datetime
|
||||
import pendulum
|
||||
import os
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
from discord.ext import commands
|
||||
from cogs.utils import config
|
||||
|
@ -30,7 +31,6 @@ extensions = ['cogs.interaction',
|
|||
bot = commands.Bot(command_prefix=config.commandPrefix, description=config.botDescription, pm_help=None)
|
||||
discord_logger = logging.getLogger('discord')
|
||||
discord_logger.setLevel(logging.WARNING)
|
||||
os.chdir(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
log = logging.getLogger()
|
||||
log.setLevel(logging.INFO)
|
||||
|
|
|
@ -124,8 +124,9 @@ class Hangman:
|
|||
Due to the fact that I might not be able to delete a message, I will PM you and ask for the phrase you want.
|
||||
The phrase needs to be under 30 characters"""
|
||||
|
||||
# Only have one hangman game per server, since anyone in a server (except the creator) can guess towards the current game
|
||||
if self.games.get(ctx.message.server.id) != None:
|
||||
# Only have one hangman game per server, since anyone
|
||||
# In a server (except the creator) can guess towards the current game
|
||||
if self.games.get(ctx.message.server.id) is not None:
|
||||
await self.bot.say("Sorry but only one Hangman game can be running per server!")
|
||||
return
|
||||
|
||||
|
@ -135,11 +136,13 @@ class Hangman:
|
|||
# Doing this so that while we wait for the phrase, another one cannot be started.
|
||||
self.games[ctx.message.server.id] = "placeholder"
|
||||
|
||||
# We want to send this message instead of just PM'ing the creator, as some people have PM's turned off/ don't pay attention to them
|
||||
# We want to send this message instead of just PM'ing the creator
|
||||
# As some people have PM's turned off/ don't pay attention to them
|
||||
await self.bot.say(
|
||||
"I have just PM'd you {}, please respond there with the phrase you want to start a new hangman game with".format(
|
||||
ctx.message.author.display_name))
|
||||
# The only reason we save this variable, is so that we can retrieve the PrivateChannel for it, for use in our wait_for_message command
|
||||
"I have just PM'd you {}, please respond there with the phrase you want to start a new"
|
||||
" hangman game with".format(ctx.message.author.display_name))
|
||||
# The only reason we save this variable, is so that we can retrieve
|
||||
# The PrivateChannel for it, for use in our wait_for_message command
|
||||
_msg = await self.bot.whisper("Please respond with the phrase you would like to use for your new hangman game\n"
|
||||
"Please note that it must be under 30 characters long")
|
||||
msg = await self.bot.wait_for_message(timeout=60.0, channel=_msg.channel, check=check)
|
||||
|
|
46
cogs/mod.py
46
cogs/mod.py
|
@ -30,7 +30,8 @@ class Mod:
|
|||
"""This command can be used to set whether or not you want user notificaitons to show
|
||||
This will save what channel you run this command in, that will be the channel used to send the notification to
|
||||
Provide on, yes, or true to set it on; otherwise it will be turned off"""
|
||||
# Join/Leave notifications can be kept separate from normal alerts, so we base this channel on it's own and not from alerts
|
||||
# Join/Leave notifications can be kept separate from normal alerts
|
||||
# So we base this channel on it's own and not from alerts
|
||||
# When mod logging becomes available, that will be kept to it's own channel if wanted as well
|
||||
on_off = ctx.message.channel.id if re.search("(on|yes|true)", on_off.lower()) else None
|
||||
notifications = config.get_content('user_notifications') or {}
|
||||
|
@ -112,7 +113,7 @@ class Mod:
|
|||
"""Sets up custom permissions on the provided command
|
||||
Format must be 'perms add <command> <permission>'
|
||||
If you want to open the command to everyone, provide 'none' as the permission"""
|
||||
|
||||
|
||||
# Since subcommands exist, base the last word in the list as the permission, and the rest of it as the command
|
||||
command = " ".join(msg[0:len(msg) - 1])
|
||||
permissions = msg[len(msg) - 1]
|
||||
|
@ -125,11 +126,15 @@ class Mod:
|
|||
perm_obj = discord.Permissions.none()
|
||||
setattr(perm_obj, permissions, True)
|
||||
perm_value = perm_obj.value
|
||||
|
||||
# This next loop ensures the command given is valid. We need to loop through commands, as self.bot.commands only includes parent commands
|
||||
# So we are splitting the command in parts, looping through the commands and getting the subcommand based on the next part
|
||||
# If we try to access commands of a command that isn't a group, we'll hit an AttributeError, meaning an invalid command was given
|
||||
# If we loop through and don't find anything, cmd will still be None, and we'll report an invalid was given as well
|
||||
|
||||
# This next loop ensures the command given is valid. We need to loop through commands
|
||||
# As self.bot.commands only includes parent commands
|
||||
# So we are splitting the command in parts, looping through the commands
|
||||
# And getting the subcommand based on the next part
|
||||
# If we try to access commands of a command that isn't a group
|
||||
# We'll hit an AttributeError, meaning an invalid command was given
|
||||
# If we loop through and don't find anything, cmd will still be None
|
||||
# And we'll report an invalid was given as well
|
||||
cmd = None
|
||||
for part in msg[0:len(msg) - 1]:
|
||||
try:
|
||||
|
@ -145,9 +150,10 @@ class Mod:
|
|||
await self.bot.say(
|
||||
"That command does not exist! You can't have custom permissions on a non-existant command....")
|
||||
return
|
||||
|
||||
|
||||
# Two cases I use should never have custom permissions setup on them, is_owner for obvious reasons
|
||||
# The other case is if I'm using the default has_permissions case, which means I do not want to check custom permissions at all
|
||||
# The other case is if I'm using the default has_permissions case
|
||||
# Which means I do not want to check custom permissions at all
|
||||
# Currently the second case is only on adding and removing permissions, to avoid abuse on these
|
||||
for check in cmd.checks:
|
||||
if "is_owner" == check.__name__ or re.search("has_permissions", str(check)) is not None:
|
||||
|
@ -158,8 +164,7 @@ class Mod:
|
|||
await self.bot.say("{} does not appear to be a valid permission! Valid permissions are: ```\n{}```"
|
||||
.format(permissions, "\n".join(valid_perms)))
|
||||
return
|
||||
|
||||
|
||||
|
||||
custom_perms = config.get_content('custom_permissions') or {}
|
||||
server_perms = custom_perms.get(ctx.message.server.id) or {}
|
||||
# Save the qualified name, so that we don't get screwed up by aliases
|
||||
|
@ -173,16 +178,17 @@ class Mod:
|
|||
@perms.command(name="remove", aliases=["delete"], pass_context=True, no_pm=True)
|
||||
@commands.has_permissions(manage_server=True)
|
||||
async def remove_perms(self, ctx, *command: str):
|
||||
"""Removes the custom permissions setup on the command specified"""
|
||||
"""Removes the custom permissions setup on the command specified"""
|
||||
custom_perms = config.get_content('custom_permissions') or {}
|
||||
server_perms = custom_perms.get(ctx.message.server.id) or {}
|
||||
if server_perms is None:
|
||||
await self.bot.say("There are no custom permissions setup on this server yet!")
|
||||
return
|
||||
|
||||
|
||||
cmd = None
|
||||
# This is the same loop as the add command, we need this to get the command object so we can get the qualified_name
|
||||
for part in msg[0:len(msg) - 1]:
|
||||
# This is the same loop as the add command, we need this to get the
|
||||
# command object so we can get the qualified_name
|
||||
for part in command[0:len(command) - 1]:
|
||||
try:
|
||||
if cmd is None:
|
||||
cmd = self.bot.commands.get(part)
|
||||
|
@ -196,12 +202,12 @@ class Mod:
|
|||
await self.bot.say(
|
||||
"That command does not exist! You can't have custom permissions on a non-existant command....")
|
||||
return
|
||||
|
||||
|
||||
command_perms = server_perms.get(cmd.qualified_name)
|
||||
if command_perms is None:
|
||||
await self.bot.say("You do not have custom permissions setup on this command!")
|
||||
return
|
||||
|
||||
|
||||
del custom_perms[ctx.message.server.id][cmd]
|
||||
config.save_content('custom_permissions', custom_perms)
|
||||
await self.bot.say("I have just removed the custom permissions for {}!".format(cmd))
|
||||
|
@ -249,7 +255,7 @@ class Mod:
|
|||
await self.bot.say(
|
||||
"This server currently has no rules on it! Can't remove something that doesn't exist bro")
|
||||
return
|
||||
|
||||
|
||||
# Get the list of rules so that we can print it if no number was provided
|
||||
# Since this is a list and not a dictionary, order is preserved, and we just need the number of the rule
|
||||
list_rules = "\n".join("{}) {}".format(num + 1, rule) for num, rule in enumerate(server_rules))
|
||||
|
@ -257,7 +263,7 @@ class Mod:
|
|||
if rule is None:
|
||||
await self.bot.say("Your rules are:\n```\n{}```Please provide the rule number"
|
||||
"you would like to remove (just the number)".format(list_rules))
|
||||
|
||||
|
||||
# All we need for the check is to ensure that the content is just a digit, that is all we need
|
||||
msg = await self.bot.wait_for_message(timeout=60.0, author=ctx.message.author, channel=ctx.message.channel,
|
||||
check=lambda m: m.content.isdigit())
|
||||
|
@ -267,7 +273,7 @@ class Mod:
|
|||
del server_rules[int(msg.content) - 1]
|
||||
rules[ctx.message.server.id] = server_rules
|
||||
config.save_content('rules', rules)
|
||||
|
||||
|
||||
# This check is just to ensure a number was provided within the list's range
|
||||
try:
|
||||
del server_rules[rule - 1]
|
||||
|
|
|
@ -10,6 +10,7 @@ import random
|
|||
|
||||
class Board:
|
||||
def __init__(self, player1, player2):
|
||||
# Our board just needs to be a 3x3 grid. To keep formatting nice, each one is going to be a space to start
|
||||
self.board = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
|
||||
|
||||
# Randomize who goes first when the board is created
|
||||
|
@ -22,81 +23,73 @@ class Board:
|
|||
self.X_turn = True
|
||||
|
||||
def full(self):
|
||||
# For this check we just need to see if there is a space anywhere, if there is then we're not full
|
||||
for row in self.board:
|
||||
if ' ' in row:
|
||||
return False
|
||||
return True
|
||||
|
||||
def can_play(self, player):
|
||||
# Simple check to see if the player is the one that's up
|
||||
if self.X_turn:
|
||||
return player == self.challengers['x']
|
||||
else:
|
||||
return player == self.challengers['o']
|
||||
|
||||
def update(self, x, y):
|
||||
# If it's x's turn, we place an x, otherwise place an o
|
||||
letter = 'x' if self.X_turn else 'o'
|
||||
# Make sure the place we're trying to update is blank, we can't override something
|
||||
if self.board[x][y] == ' ':
|
||||
self.board[x][y] = letter
|
||||
else:
|
||||
return False
|
||||
# If we were succesful in placing the piece, we need to switch whose turn it is
|
||||
self.X_turn = not self.X_turn
|
||||
return True
|
||||
|
||||
def check(self):
|
||||
# Checking all possiblities will be fun...
|
||||
# First base off the top-left corner, see if any possiblities with that match
|
||||
# We need to also make sure that the place is not blank, so that 3 in a row that are blank doesn't cause a 'win'
|
||||
# Top-left, top-middle, top right
|
||||
if self.board[0][0] == self.board[0][1] and self.board[0][0] == self.board[0][2] and self.board[0][0] != ' ':
|
||||
if self.board[0][0] == 'x':
|
||||
return self.challengers['x']
|
||||
else:
|
||||
return self.challengers['o']
|
||||
return self.challengers[self.board[0][0]]
|
||||
# Top-left, middle-left, bottom-left
|
||||
if self.board[0][0] == self.board[1][0] and self.board[0][0] == self.board[2][0] and self.board[0][0] != ' ':
|
||||
if self.board[0][0] == 'x':
|
||||
return self.challengers['x']
|
||||
else:
|
||||
return self.challengers['o']
|
||||
return self.challengers[self.board[0][0]]
|
||||
# Top-left, middle, bottom-right
|
||||
if self.board[0][0] == self.board[1][1] and self.board[0][0] == self.board[2][2] and self.board[0][0] != ' ':
|
||||
if self.board[0][0] == 'x':
|
||||
return self.challengers['x']
|
||||
else:
|
||||
return self.challengers['o']
|
||||
return self.challengers[self.board[0][0]]
|
||||
|
||||
# Next check the top-right corner, not re-checking the last possiblity that included it
|
||||
# Top-right, middle-right, bottom-right
|
||||
if self.board[0][2] == self.board[1][2] and self.board[0][2] == self.board[2][2] and self.board[0][2] != ' ':
|
||||
if self.board[0][2] == 'x':
|
||||
return self.challengers['x']
|
||||
else:
|
||||
return self.challengers['o']
|
||||
return self.challengers[self.board[0][2]]
|
||||
# Top-right, middle, bottom-left
|
||||
if self.board[0][2] == self.board[1][1] and self.board[0][2] == self.board[2][0] and self.board[0][2] != ' ':
|
||||
if self.board[0][2] == 'x':
|
||||
return self.challengers['x']
|
||||
else:
|
||||
return self.challengers['o']
|
||||
return self.challengers[self.board[0][2]]
|
||||
|
||||
# Next up, bottom-right corner, only one possiblity to check here, other two have been checked
|
||||
# Bottom-right, bottom-middle, bottom-left
|
||||
if self.board[2][2] == self.board[2][1] and self.board[2][2] == self.board[2][0] and self.board[2][2] != ' ':
|
||||
if self.board[2][2] == 'x':
|
||||
return self.challengers['x']
|
||||
else:
|
||||
return self.challengers['o']
|
||||
return self.challengers[self.board[2][2]]
|
||||
|
||||
# No need to check the bottom-left, all posiblities have been checked now
|
||||
# Base things off the middle now, as we only need the two 'middle' possiblites that aren't diagonal
|
||||
# Top-middle, middle, bottom-middle
|
||||
if self.board[1][1] == self.board[0][1] and self.board[1][1] == self.board[2][1] and self.board[1][1] != ' ':
|
||||
if self.board[1][1] == 'x':
|
||||
return self.challengers['x']
|
||||
else:
|
||||
return self.challengers['o']
|
||||
return self.challengers[self.board[1][1]]
|
||||
# Left-middle, middle, right-middle
|
||||
if self.board[1][1] == self.board[1][0] and self.board[1][1] == self.board[1][2] and self.board[1][1] != ' ':
|
||||
if self.board[1][1] == 'x':
|
||||
return self.challengers['x']
|
||||
else:
|
||||
return self.challengers['o']
|
||||
return self.challengers[self.board[1][1]]
|
||||
|
||||
# Otherwise nothing has been found, return None
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
# Simple formatting here when you look at it, enough spaces to even out where everything is
|
||||
# Place whatever is at the grid in place, whether it's x, o, or blank
|
||||
_board = " {} | {} | {}\n".format(self.board[0][0], self.board[0][1], self.board[0][2])
|
||||
_board += "———————————————\n"
|
||||
_board += " {} | {} | {}\n".format(self.board[1][0], self.board[1][1], self.board[1][2])
|
||||
|
@ -105,6 +98,50 @@ class Board:
|
|||
return "```\n{}```".format(_board)
|
||||
|
||||
|
||||
def update_records(winner, loser):
|
||||
# This is the exact same formula as the battling update.
|
||||
# The only difference is I use the word "match" instead of "battle"
|
||||
matches = config.get_content('tictactoe')
|
||||
if matches is None:
|
||||
matches = {winner.id: "1-0", loser.id: "0-1"}
|
||||
|
||||
winner_stats = matches.get(winner.id) or {}
|
||||
winner_rating = winner_stats.get('rating') or 1000
|
||||
|
||||
loser_stats = matches.get(loser.id) or {}
|
||||
loser_rating = loser_stats.get('rating') or 1000
|
||||
|
||||
difference = abs(winner_rating - loser_rating)
|
||||
rating_change = 0
|
||||
count = 25
|
||||
while count <= difference:
|
||||
if count > 300:
|
||||
break
|
||||
rating_change += 1
|
||||
count += 25
|
||||
|
||||
if winner_rating > loser_rating:
|
||||
winner_rating += 16 - rating_change
|
||||
loser_rating -= 16 - rating_change
|
||||
else:
|
||||
winner_rating += 16 + rating_change
|
||||
loser_rating -= 16 + rating_change
|
||||
|
||||
winner_wins = winner_stats.get('wins') or 0
|
||||
winner_losses = winner_stats.get('losses') or 0
|
||||
loser_wins = loser_stats.get('wins') or 0
|
||||
loser_losses = loser_stats.get('losses') or 0
|
||||
winner_wins += 1
|
||||
loser_losses += 1
|
||||
|
||||
winner_stats = {'wins': winner_wins, 'losses': winner_losses, 'rating': winner_rating}
|
||||
loser_stats = {'wins': loser_wins, 'losses': loser_losses, 'rating': loser_rating}
|
||||
matches[winner.id] = winner_stats
|
||||
matches[loser.id] = loser_stats
|
||||
|
||||
return config.save_content('tictactoe', matches)
|
||||
|
||||
|
||||
class TicTacToe:
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
|
@ -116,47 +153,6 @@ class TicTacToe:
|
|||
# Return whoever is x's so that we know who is going first
|
||||
return self.boards[server_id].challengers['x']
|
||||
|
||||
def update_records(self, winner, loser):
|
||||
matches = config.get_content('tictactoe')
|
||||
if matches is None:
|
||||
matches = {winner.id: "1-0", loser.id: "0-1"}
|
||||
|
||||
winner_stats = matches.get(winner.id) or {}
|
||||
winner_rating = winner_stats.get('rating') or 1000
|
||||
|
||||
loser_stats = matches.get(loser.id) or {}
|
||||
loser_rating = loser_stats.get('rating') or 1000
|
||||
|
||||
difference = abs(winner_rating - loser_rating)
|
||||
rating_change = 0
|
||||
count = 25
|
||||
while count <= difference:
|
||||
if count > 300:
|
||||
break
|
||||
rating_change += 1
|
||||
count += 25
|
||||
|
||||
if winner_rating > loser_rating:
|
||||
winner_rating += 16 - rating_change
|
||||
loser_rating -= 16 - rating_change
|
||||
else:
|
||||
winner_rating += 16 + rating_change
|
||||
loser_rating -= 16 + rating_change
|
||||
|
||||
winner_wins = winner_stats.get('wins') or 0
|
||||
winner_losses = winner_stats.get('losses') or 0
|
||||
loser_wins = loser_stats.get('wins') or 0
|
||||
loser_losses = loser_stats.get('losses') or 0
|
||||
winner_wins += 1
|
||||
loser_losses += 1
|
||||
|
||||
winner_stats = {'wins': winner_wins, 'losses': winner_losses, 'rating': winner_rating}
|
||||
loser_stats = {'wins': loser_wins, 'losses': loser_losses, 'rating': loser_rating}
|
||||
matches[winner.id] = winner_stats
|
||||
matches[loser.id] = loser_stats
|
||||
|
||||
return config.save_content('tictactoe', matches)
|
||||
|
||||
@commands.group(pass_context=True, aliases=['tic', 'tac', 'toe'], no_pm=True, invoke_without_command=True)
|
||||
@checks.custom_perms(send_messages=True)
|
||||
async def tictactoe(self, ctx, *, option: str):
|
||||
|
@ -166,9 +162,12 @@ class TicTacToe:
|
|||
Provide top, left, bottom, right, middle as you want to mark where to play on the board"""
|
||||
player = ctx.message.author
|
||||
board = self.boards.get(ctx.message.server.id)
|
||||
# Need to make sure the board exists before allowing someone to play
|
||||
if not board:
|
||||
await self.bot.say("There are currently no Tic-Tac-Toe games setup!")
|
||||
return
|
||||
# Now just make sure the person can play, this will fail if o's are up and x tries to play
|
||||
# Or if someone else entirely tries to play
|
||||
if not board.can_play(player):
|
||||
await self.bot.say("You cannot play right now!")
|
||||
return
|
||||
|
@ -180,13 +179,14 @@ class TicTacToe:
|
|||
left = re.search('left', option)
|
||||
right = re.search('right', option)
|
||||
|
||||
# Check if what was given was valid
|
||||
# Just a bit of logic to ensure nothing that doesn't make sense is given
|
||||
if top and bottom:
|
||||
await self.bot.say("That is not a valid location! Use some logic, come on!")
|
||||
return
|
||||
if left and right:
|
||||
await self.bot.say("That is not a valid location! Use some logic, come on!")
|
||||
return
|
||||
# Make sure at least something was given
|
||||
if not top and not bottom and not left and not right and not middle:
|
||||
await self.bot.say("Please provide a valid location to play!")
|
||||
return
|
||||
|
@ -200,30 +200,46 @@ class TicTacToe:
|
|||
y = 0
|
||||
if right:
|
||||
y = 2
|
||||
# If middle was given and nothing else, we need the exact middle
|
||||
if middle and not (top or bottom or left or right):
|
||||
x = 1
|
||||
y = 1
|
||||
# If just top or bottom was given, we assume this means top-middle or bottom-middle
|
||||
# We don't need to do anything fancy with top/bottom as it's already assigned, just assign middle
|
||||
if (top or bottom) and not (left or right):
|
||||
y = 1
|
||||
# If just left or right was given, we assume this means left-middle or right-middle
|
||||
# We don't need to do anything fancy with left/right as it's already assigned, just assign middle
|
||||
elif (left or right) and not (top or bottom):
|
||||
x = 1
|
||||
|
||||
# If all checks have been made, x and y should now be defined correctly based on the matches, and we can go ahead and:
|
||||
# If all checks have been made, x and y should now be defined
|
||||
# Correctly based on the matches, and we can go ahead and update the board
|
||||
# We've already checked if the author can play, so there's no need to make any additional checks here
|
||||
# board.update will handle which letter is placed
|
||||
# If it returns false however, then someone has already played in that spot and nothing was updated
|
||||
if not board.update(x, y):
|
||||
await self.bot.say("Someone has already played there!")
|
||||
return
|
||||
# Next check if there's a winner
|
||||
winner = board.check()
|
||||
if winner:
|
||||
# Get the loser based on whether or not the winner is x's
|
||||
# If the winner is x's, the loser is o's...obviously, and vice-versa
|
||||
loser = board.challengers['x'] if board.challengers['x'] != winner else board.challengers['o']
|
||||
await self.bot.say("{} has won this game of TicTacToe, better luck next time {}".format(winner.display_name,
|
||||
loser.display_name))
|
||||
|
||||
self.update_records(winner, loser)
|
||||
# Handle updating ratings based on the winner and loser
|
||||
update_records(winner, loser)
|
||||
# This game has ended, delete it so another one can be made
|
||||
del self.boards[ctx.message.server.id]
|
||||
else:
|
||||
# If no one has won, make sure the game is not full. If it has, delete the board and say it was a tie
|
||||
if board.full():
|
||||
await self.bot.say("This game has ended in a tie!")
|
||||
del self.boards[ctx.message.server.id]
|
||||
# If no one has won, and the game has not ended in a tie, print the new updated board
|
||||
# TODO: edit in place instead of printing?
|
||||
else:
|
||||
await self.bot.say(str(board))
|
||||
|
||||
|
@ -232,12 +248,17 @@ class TicTacToe:
|
|||
async def start_game(self, ctx, player2: discord.Member):
|
||||
"""Starts a game of tictactoe with another player"""
|
||||
player1 = ctx.message.author
|
||||
# For simplicities sake, only allow one game on a server at a time.
|
||||
# Things can easily get confusing (on the server's end) if we allow more than one
|
||||
if self.boards.get(ctx.message.server.id) is not None:
|
||||
await self.bot.say("Sorry but only one Tic-Tac-Toe game can be running per server!")
|
||||
return
|
||||
# Create the board and return who has been decided to go first
|
||||
x_player = self.create(ctx.message.server.id, player1, player2)
|
||||
fmt = "A tictactoe game has just started between {} and {}".format(player1.display_name, player2.display_name)
|
||||
# Print the board too just because
|
||||
fmt += str(self.boards[ctx.message.server.id])
|
||||
# We don't need to do anything weird with assigning x_player to something, it is already a member object, just use it
|
||||
fmt += "I have decided at random, and {} is going to be x's this game. It is your turn first!".format(
|
||||
x_player.display_name)
|
||||
await self.bot.say(fmt)
|
||||
|
|
Loading…
Reference in a new issue