You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Fox-V3/fight/fight.py

1214 lines
42 KiB

import math
import discord
import asyncio
from discord.ext import commands
from redbot.core import Config
from redbot.core import checks
from redbot.core.utils.chat_formatting import box
from redbot.core.utils.chat_formatting import pagify
# from typing import Union
# 0 - Robin, 1 - Single, 2 - Double, 3 - Triple, 4 - Guarantee, 5 - Compass
T_TYPES = {
0: "Round Robin",
1: "Single Elimination",
2: "Double Elimination",
3: "Triple Elimination",
4: "3 Game Guarantee",
5: "Compass Draw",
}
class Fight:
"""Cog for organizing fights"""
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=49564952847684, force_registration=True)
default_global = {
"srtracker": {},
"win": None,
"winu": None,
"loss": None,
"lossu": None,
"dispute": None,
"disputeu": None,
}
default_guild = {
"current": None,
"tourneys": {},
"settings": {
"selfreport": True,
"reportchnnl": None,
"announcechnnl": None,
"admin": None,
},
"emoji": {"nums": [], "undo": None, "appr": None},
}
self.default_tourney = {
"PLAYERS": [],
"NAME": "Tourney 0",
"RULES": {"BESTOF": 1, "BESTOFFINAL": 1, "TYPE": 0},
"TYPEDATA": {},
"OPEN": False,
"WINNER": None,
}
self.default_match = {
"TEAM1": [],
"TEAM2": [],
"SCORE1": None,
"SCORE2": None,
"USERSCORE1": {"SCORE1": None, "SCORE2": None},
"USERSCORE2": {"SCORE1": None, "SCORE2": None},
"WINNER": None,
"DISPUTE": False,
}
self.default_tracker = {"TID": None, "MID": None, "RID": None, "GUILDID": None}
self.config.register_global(**default_global)
self.config.register_guild(**default_guild)
# ************************Fight command group start************************
@commands.group()
@commands.guild_only()
async def fight(self, ctx):
"""Participate in active fights!"""
# guild = ctx.message.guild
if not await self._activefight(ctx):
await ctx.send("No tournament currently running!")
else:
await ctx.send("Current tournament ID: " + await self._activefight(ctx))
if ctx.invoked_subcommand is None:
pass
# await ctx.send("I can do stuff!")
@fight.command(name="join")
async def fight_join(self, ctx, user: discord.Member = None):
"""Join the active fight"""
# guild = ctx.message.guild
if not user:
user = ctx.author
curr_fight = await self._getcurrentfight(ctx)
t_id = await self._activefight(ctx)
if not curr_fight:
await ctx.send("No tournament currently running!")
return
if not curr_fight["OPEN"]:
await ctx.send("Tournament currently not accepting new players")
return
if await self._infight(ctx, t_id, user.id):
await ctx.send("You are already in this tournament!")
return
curr_fight["PLAYERS"].append(user.id)
await self._save_fight(ctx, t_id, curr_fight)
await ctx.send("User has been added to tournament")
# @fight.command(name="score")
# async def fight_score(self, ctx, tID=None, score1=None, score2=None):
# """Enters score for current match, or for passed tournament ID"""
# # guild = ctx.message.guild
# # user = ctx.message.author
# currFight = await self._getcurrentfight(ctx)
# if not currFight:
# await ctx.send("No tournament currently running!")
# return
# if not tID:
# tID = await self._activefight(ctx)
# if not await self._infight(ctx, tID, ctx.author.id):
# await ctx.send("You are not in a current tournament")
# return
# if not currFight["TYPEDATA"]:
# await ctx.send("Tournament has not started yet")
# return
# mID = await self._parseuser(ctx.guild, tID, ctx.author.id)
# if not mID:
# await ctx.send("You have no match to update!")
# return
# if currFight["RULES"]["TYPE"] == 0: # Round-Robin
# await self._rr_score(ctx, tID, mID, score1, score2)
@fight.command(name="leave")
async def fight_leave(self, ctx, t_id=None, user: discord.Member = None):
"""Forfeit your match and all future matches"""
# guild = ctx.message.guild
if not user:
user = ctx.author
if not t_id:
t_id = await self._activefight(ctx)
await ctx.send("Todo Leave")
@fight.group(name="bracket")
async def fight_bracket(self, ctx, t_id):
"""Shows your current match your next opponent,
run [p]fight bracket full to see all matches"""
# ToDo Bracket
await ctx.send("Todo Bracket")
@fight_bracket.command(name="full")
async def fight_bracket_full(self, ctx, t_id):
"""Shows the full bracket"""
# ToDo Bracket Full
await ctx.send("Todo Bracket Full")
# **********************Fightset command group start*********************
@commands.group()
@commands.guild_only()
@checks.mod_or_permissions(administrator=True)
async def fadmin(self, ctx):
"""Admin command for managing the current tournament"""
if ctx.invoked_subcommand is None:
pass
@fadmin.command(name="score")
async def fadmin_score(self, ctx, m_id, score1, score2):
"""Set's the score for matchID and clears disputes"""
curr_fight = await self._getcurrentfight(ctx)
t_id = await self._activefight(ctx)
if not curr_fight:
await ctx.send("No tournament currently running!")
return
# ToDo allow score adjustment
# **********************Fightset command group start*********************
@commands.group(aliases=["setfight"])
@commands.guild_only()
@checks.mod_or_permissions(administrator=True)
async def fightset(self, ctx):
"""Admin command for starting or managing tournaments"""
if ctx.invoked_subcommand is None:
pass
# await ctx.send("I can do stuff!")
@fightset.command(name="emoji")
async def fightset_emoji(self, ctx):
"""Set the global reaction emojis for reporting matches"""
message = await ctx.send("Emoji Tests")
message2 = await ctx.send("Secondary Emoji Tests")
needed = ["reporting a win", "reporting a loss", "disputing results"]
for need in needed:
try:
emoji, actual_emoji, is_unicode = await self._wait_for_emoji(ctx, need)
except asyncio.TimeoutError:
await ctx.send("You didn't respond in time, please redo this command.")
return
try:
await message.add_reaction(actual_emoji)
except discord.HTTPException:
await ctx.send(
"I can't add that emoji because I'm not in the guild that" " owns it."
)
return
if need == "reporting a win":
win_emoji = emoji
win_unicode = is_unicode
if need == "reporting a loss":
loss_emoji = emoji
loss_unicode = is_unicode
if need == "disputing results":
dispute_emoji = emoji
dis_unicode = is_unicode
await self.config.win.set(win_emoji)
await self.config.winu.set(win_unicode)
await self.config.loss.set(loss_emoji)
await self.config.lossu.set(loss_unicode)
await self.config.dispute.set(dispute_emoji)
await self.config.disputeu.set(dis_unicode)
await self._add_wld(message2)
await ctx.send("Success")
@fightset.command(name="reset")
async def fightset_reset(self, ctx):
"""Clears all data, be careful!"""
await self.config.clear_all()
await ctx.send("Success")
@fightset.command(name="trackreset")
async def fightset_trackreset(self, ctx):
"""Clears all message trackers!"""
await self.config.srtracker.set({})
await ctx.send("Success")
@fightset.command(name="bestof")
async def fightset_bestof(self, ctx, incount, t_id=None):
"""Adjust # of games played per match. Must be an odd number"""
# guild = ctx.message.guild
if not t_id and not await self._activefight(ctx):
await ctx.send("No active fight to adjust")
return
if not t_id:
t_id = await self._activefight(ctx)
curr_fight = await self._getfight(ctx.guild, t_id)
try:
num = int(incount)
except:
await ctx.send("That is not a number")
return
if num % 2 != 1:
await ctx.send("Must be an odd number")
return
if num < 1:
await ctx.send("Must be greater than 0, idiot")
return
if num > 17:
await ctx.send("I can't go that high! Max 17")
return
curr_fight["RULES"]["BESTOF"] = num
await self._save_fight(ctx, t_id, curr_fight)
await ctx.send("Tourney ID " + t_id + " is now Best of " + str(num))
@fightset.command(name="bestoffinal")
async def fightset_bestoffinal(self, ctx, incount, t_id=None):
"""Adjust # of games played in finals. Must be an odd number
(Does not apply to tournament types without finals, such as Round Robin)"""
# guild = ctx.message.guild
if not t_id and not await self._activefight(ctx):
await ctx.send("No active fight to adjust")
return
if not t_id:
t_id = await self._activefight(ctx)
curr_fight = await self._getfight(ctx.guild, t_id)
try:
num = int(incount)
except:
await ctx.send("That is not a number")
return
if num % 2 != 1:
await ctx.send("Must be an odd number")
return
if num < 1:
await ctx.send("Must be greater than 0, idiot")
return
curr_fight["RULES"]["BESTOFFINAL"] = num
await self._save_fight(ctx, t_id, curr_fight)
await ctx.send("Tourney ID " + t_id + " is now Best of " + str(num) + " in the Finals")
@fightset.command(name="current")
async def fightset_current(self, ctx, t_id):
"""Sets the current tournament to passed ID"""
# guild = ctx.message.guild
curr_fight = await self._getfight(ctx.guild, t_id)
if not curr_fight:
await ctx.send("No tourney found with that ID")
return
# self.the_data[guild.id]["CURRENT"] = t_id
# self.save_data()
await self.config.guild(ctx.guild).current.set(t_id)
await ctx.send("Current tournament set to " + t_id)
@fightset.command(name="list")
async def fightset_list(self, ctx):
"""Lists all current and past fights"""
# guild = ctx.message.guild
for page in pagify(str(await self.config.guild(ctx.guild).tourneys())):
await ctx.send(box(page))
await ctx.send("Done")
@fightset.command(name="test")
async def fightset_test(self, ctx):
"""testing"""
await ctx.send(str(await self.config.all_guilds()))
@fightset.command(name="open")
async def fightset_open(self, ctx):
"""Toggles the open status of current tournament"""
# guild = ctx.message.guild
if not await self._activefight(ctx):
await ctx.send("No active fight to adjust")
return
t_id = await self._activefight(ctx)
curr_fight = await self._getcurrentfight(ctx)
curr_fight["OPEN"] = not curr_fight["OPEN"]
await self._save_fight(ctx, t_id, curr_fight)
await ctx.send("Tournament Open status is now set to: " + str(curr_fight["OPEN"]))
@fightset.command(name="name")
async def fightset_name(self, ctx, inname, t_id=None):
"""Renames the tournament"""
# guild = ctx.message.guild
if not t_id and not await self._activefight(ctx):
await ctx.send("No active fight to adjust")
return
if not t_id:
t_id = await self._activefight(ctx)
curr_fight = await self._getfight(ctx.guild, t_id)
curr_fight["NAME"] = inname
await self._save_fight(ctx, t_id, curr_fight)
await ctx.send("Tourney ID " + t_id + " is now called " + inname)
@fightset.command(name="start")
async def fightset_start(self, ctx):
"""Starts the current tournament, must run setup first"""
def check(m): # Check Message from author
return m.author == ctx.author and m.channel == ctx.channel
curr_fight = await self._getcurrentfight(ctx)
t_id = await self._activefight(ctx)
if not t_id:
await ctx.send("No current fight to start")
return
if (await self.config.win()) is None: # Emoji not setup
await ctx.send("Emojis have not been configured, see `[p]fightset emoji`")
return
if (await self._get_announcechnnl(ctx.guild)) is None: # Announcechnnl not setup
await ctx.send(
"Announcement channel has not been configured, see `[p]fightset guild announce`"
)
return
if (await self._get_reportchnnl(ctx.guild)) is None: # Reportchnnl not setup
await ctx.send(
"Self-Report channel has not been configured, see `[p]fightset guild report`"
)
return
if curr_fight["TYPEDATA"]: # Empty dicionary {} resolves to False
await ctx.send(
"Looks like this tournament has already started.\nDo you want to delete all match data and restart? "
"(yes/no)"
)
try:
answer = await self.bot.wait_for("message", check=check, timeout=120)
except asyncio.TimeoutError:
await ctx.send("Cancelled due to timeout")
return
if not answer.content or answer.content.upper() not in ["YES", "Y"]:
await ctx.send("Cancelled")
return
curr_fight["OPEN"] = False # first close the tournament
await self._save_fight(ctx, t_id, curr_fight)
if curr_fight["RULES"]["TYPE"] == 0: # Round-Robin
await self._rr_start(ctx, t_id)
@fightset.command(name="setup")
async def fightset_setup(self, ctx):
"""Setup a new tournament!
Default settings are as follows
Name: Tourney # (counts from 0)
Best of: 1
Best of (final): 1
Self Report: True
Type: 0 (Round Robin)"""
# guild = ctx.message.guild
# currServ = self.the_data[guild.id]
t_id = str(
len(await self.config.guild(ctx.guild).tourneys())
) # Can just be len without +1, tourney 0 makes len 1, tourney 1 makes len 2, etc
# currServ["CURRENT"] = t_id
curr_fight = self.default_tourney.copy()
curr_fight["NAME"] = "Tourney " + str(t_id)
await self._save_fight(ctx, t_id, curr_fight)
await ctx.send("Tournament has been created!\n\n" + str(curr_fight))
await ctx.send(
"Adjust settings as necessary, then open the tournament with [p]fightset open"
)
@fightset.command(name="stop")
async def fightset_stop(self, ctx):
"""Stops current tournament"""
def check(m): # Check Message from author
return m.author == ctx.author and m.channel == ctx.channel
# guild = ctx.message.guild
if not await self._activefight(ctx):
await ctx.send("No active fight to adjust")
return
# author = ctx.message.author
# currServ = self.the_data[guild.id]
await ctx.send(
"Current fight ID is "
+ str(await self.config.guild(ctx.guild).current())
+ "\nOkay to stop? (yes/no)"
)
try:
answer = await self.bot.wait_for("message", check=check, timeout=120)
except asyncio.TimeoutError:
await ctx.send("Cancelled due to timeout")
return
if not answer.content or answer.content.upper() not in ["YES", "Y"]:
await ctx.send("Cancelled")
return
await self.config.guild(ctx.guild).current.set(None)
await ctx.send("Fight has been stopped")
# ***************************Fightset_guild command group start**************************
@fightset.group(name="guild")
async def fightset_guild(self, ctx):
"""Adjust guild wide settings"""
if ctx.invoked_subcommand is None or isinstance(ctx.invoked_subcommand, commands.Group):
pass
@fightset_guild.command(name="selfreport")
async def fightset_guild_selfreport(self, ctx):
"""Toggles the ability to self-report scores for all tournaments"""
curflag = await self.config.guild(ctx.guild).settings.selfreport()
await self.config.guild(ctx.guild).settings.selfreport.set(not curflag)
await ctx.send("Self-Reporting ability is now set to: " + str(not curflag))
@fightset_guild.command(name="report")
async def fightset_guild_report(self, ctx, channel: discord.TextChannel = None):
"""Set the channel for self-reporting matches"""
if channel is None:
channel = ctx.channel
await self.config.guild(ctx.guild).settings.reportchnnl.set(channel.id)
channel = await self._get_reportchnnl(ctx.guild)
await ctx.send("Self-Reporting Channel is now set to: " + channel.mention)
@fightset_guild.command(name="announce")
async def fightset_guild_announce(self, ctx, channel: discord.TextChannel = None):
"""Set the channel for tournament announcements"""
if channel is None:
channel = ctx.channel
await self.config.guild(ctx.guild).settings.announcechnnl.set(channel.id)
channel = await self._get_announcechnnl(ctx.guild)
await ctx.send("Announcement Channel is now set to: " + channel.mention)
@fightset_guild.command(name="setadmin")
async def fightset_guild_setadmin(self, ctx, role: discord.Role = None):
"""Chooses the tournament-admin role. CAREFUL: This grants the ability to override self-reported scores!"""
await self.config.guild(ctx.guild).settings.admin.set(role.id)
await ctx.send("Tournament Admin role is now set to: " + role.mention)
# **********************Private command group start*********************
async def _add_wld(self, message: discord.Message):
"""Adds assigned Win-Loss-Dispute reactions to message"""
win = await self.config.win()
loss = await self.config.loss()
dispute = await self.config.dispute()
if not (await self.config.winu()): # If not unicode
win = self.bot.get_emoji(win)
if not (await self.config.lossu()):
loss = self.bot.get_emoji(loss)
if not (await self.config.disputeu()):
dispute = self.bot.get_emoji(dispute)
await message.add_reaction(win)
await message.add_reaction(loss)
await message.add_reaction(dispute)
async def _get_win_str(self):
"""Returns win emoji ready for str"""
win = await self.config.win()
if not (await self.config.winu()): # If not unicode
win = str(self.bot.get_emoji(win))
return win
async def _get_loss_str(self):
"""Returns loss emoji ready for str"""
loss = await self.config.loss()
if not (await self.config.lossu()):
loss = str(self.bot.get_emoji(loss))
return loss
async def _get_dispute_str(self):
"""Returns dispute emoji ready for str"""
dispute = await self.config.dispute()
if not (await self.config.disputeu()):
dispute = str(self.bot.get_emoji(dispute))
return dispute
async def _wait_for_emoji(self, ctx: commands.Context, messagetext):
"""
Asks the user to react to this message and returns the emoji string if unicode
or ID if custom.
:param ctx:
:raises asyncio.TimeoutError:
If the user does not respond in time.
:return:
"""
if messagetext:
message = await ctx.send(
"Please react to this message with the reaction you"
" would like for " + messagetext + ", you have 20 seconds to"
" respond."
)
else:
message = await ctx.send(
"Please react to this message with the reaction you"
" would like, you have 20 seconds to"
" respond."
)
def _wait_check(react, user):
msg = react.message
return msg.id == message.id and user.id == ctx.author.id
reaction, _ = await ctx.bot.wait_for("reaction_add", check=_wait_check, timeout=20)
try:
ret = reaction.emoji.id
is_unicode = False
except AttributeError:
# The emoji is unicode
ret = reaction.emoji
is_unicode = True
return ret, reaction.emoji, is_unicode
async def _save_fight(self, ctx, t_id, curr_fight):
"""Save a passed fight"""
guild_group = self.config.guild(ctx.guild)
async with guild_group.tourneys() as allTourney:
allTourney[t_id] = curr_fight
# allTourney = await self.config.guild(ctx.guild).tourneys()
# allTourney[t_id] = curr_fight
# await self.config.guild(ctx.guild).tourneys.set(allTourney)
async def _save_tracker(self, ctx, messageid: int, match_data):
"""Save a passed fight"""
async with self.config.srtracker() as allTracker:
allTracker[str(messageid)] = match_data
# allTracker = dict(await self.config.srtracker())
# allTracker[messageid] = match_data
# await self.config.srtracker.set(allTracker)
async def _guildsettings(self, ctx: commands.Context):
"""Returns the dictionary of guild settings"""
# return self.the_data[guildID]["SETTINGS"]
return await self.config.guild(ctx.guild).settings()
async def _messagetracker(self, ctx: commands.Context):
"""Returns the dictionary of message tracking"""
# return self.the_data[guildID]["SRTRACKER"]
return await self.config.srtracker()
async def _activefight(self, ctx: commands.Context):
"""Returns id for active fight, or None if no active fight"""
# return self.the_data[guildID]["CURRENT"]
return await self.config.guild(ctx.guild).current()
async def _infight(self, ctx: commands.Context, t_id, userid):
"""Checks if passed member is already in the tournament"""
# return userid in self.the_data[guildID]["TOURNEYS"][t_id]["PLAYERS"]
return userid in (await self.config.guild(ctx.guild).tourneys())[t_id]["PLAYERS"]
async def _embed_tourney(self, ctx, t_id):
"""Prints a pretty embed of the tournament"""
# _placeholder Todo
pass
async def _comparescores(self, ctx):
"""Checks user submitted scores for inconsistancies"""
# _comparescores Todo
pass
async def _parseuser(self, guild: discord.Guild, t_id, userid):
"""Finds user in the tournament"""
# if self._getfight(guildID, t_id)["RULES"]["TYPE"] == 0: # RR
the_fight = await self._getfight(guild, t_id)
if userid not in the_fight["PLAYERS"]: # Shouldn't happen, _infight check first
return False
if the_fight["RULES"]["TYPE"] == 0:
return await self._rr_parseuser(guild, t_id, userid)
return False
def _get_team(self, ctx: commands.Context, teaminfo):
"""Team info is a list of userid's. Returns a list of user objects"""
outlist = []
for playerid in teaminfo:
outlist.append(self._get_user_from_id(playerid))
return outlist
# async def _getsettings(self, ctx: commands.Context):
# # return self.the_data[guildID]["SETTINGS"]
# return await self.config.guild(ctx.guild).settings()
# async def _get_message_from_id_old(self, channelid, messageid):
# return await self.bot.get_message(self._get_channel_from_id(channelid), messageid)
async def _get_message_from_id(self, guild: discord.Guild, message_id: int):
"""
Tries to find a message by ID in the current guild context.
:param ctx:
:param message_id:
:return:
"""
for channel in guild.text_channels:
try:
return await channel.get_message(message_id)
except discord.NotFound:
pass
except AttributeError: # VoiceChannel object has no attribute 'get_message'
pass
return None
async def _get_announcechnnl(self, guild: discord.Guild):
channelid = await self.config.guild(guild).settings.announcechnnl()
channel = self._get_channel_from_id(channelid)
return channel
async def _get_reportchnnl(self, guild: discord.Guild):
channelid = await self.config.guild(guild).settings.reportchnnl()
channel = self._get_channel_from_id(channelid)
return channel
def _get_channel_from_id(self, channelid):
return self.bot.get_channel(channelid)
def _get_user_from_id(self, userid):
# guild = self._get_guild_from_id(guildID)
# return discord.utils.get(guild.members, id=userid)
return self.bot.get_user(userid)
def _get_guild_from_id(self, guild_id):
return self.bot.get_guild(guild_id)
async def _getfight(self, guild: discord.Guild, t_id):
# return self.the_data[guildID]["TOURNEYS"][t_id]
return (await self.config.guild(guild).tourneys())[t_id]
async def _getcurrentfight(self, ctx: commands.Context):
# if not self._activefight(guildID):
# return None
# return self._getfight(guildID, self._activefight(guildID))
is_active = await self._activefight(ctx)
if not is_active:
return None
return await self._getfight(ctx.guild, is_active)
async def _report_win(self, guild: discord.Guild, t_id, m_id, member: discord.Member):
"""Reports a win for member in match"""
the_fight = await self._getfight(guild, t_id)
if member.id not in the_fight["PLAYERS"]: # Shouldn't happen
return False
if the_fight["RULES"]["TYPE"] == 0:
return await self._rr_report_wl(guild, t_id, m_id, member, True)
async def _report_loss(self, guild: discord.Guild, t_id, m_id, member: discord.Member):
"""Reports a win for member in match"""
the_fight = await self._getfight(guild, t_id)
if member.id not in the_fight["PLAYERS"]: # Shouldn't happen
return False
if the_fight["RULES"]["TYPE"] == 0:
return await self._rr_report_wl(guild, t_id, m_id, member, False)
async def _report_dispute(self, guild: discord.Guild, t_id, m_id):
"""Reports a win for member in match"""
the_fight = await self._getfight(guild, t_id)
# ToDo: What is this supposed to be again?
# if member.id not in the_fight["PLAYERS"]: # Shouldn't happen
# return False
if the_fight["RULES"]["TYPE"] == 0:
return await self._rr_report_dispute(guild, t_id, m_id)
return False
# *********** References to "TYPEDATA" must be done per tournament mode (Below this line) *******
# **********************Single Elimination***************************
async def _elim_setup(self, t_id):
# ToDo Elim setup
pass
async def _elim_start(self, t_id):
# ToDo Elim start
pass
async def _elim_update(self, m_id):
# ToDo elim update
pass
# **********************Round-Robin**********************************
async def _rr_report_wl(self, guild: discord.Guild, t_id, m_id, user: discord.Member, is_win):
"""User reports a win or loss for member in match"""
the_fight = await self._getfight(guild, t_id)
teamnum = await self._rr_matchperms(guild, t_id, user.id, m_id)
# _rr_parseuser has already be run in on_raw_reaction_add, should be safe to proceed without checking again
if (is_win and teamnum == 1) or (not is_win and teamnum == 2):
score1 = math.ceil(the_fight["RULES"]["BESTOF"] / 2)
score2 = 0
else:
score1 = 0
score2 = math.ceil(the_fight["RULES"]["BESTOF"] / 2)
if teamnum == 1:
the_fight["TYPEDATA"]["MATCHES"][m_id]["USERSCORE1"]["SCORE1"] = score1
the_fight["TYPEDATA"]["MATCHES"][m_id]["USERSCORE1"]["SCORE2"] = score2
if teamnum == 2:
the_fight["TYPEDATA"]["MATCHES"][m_id]["USERSCORE2"]["SCORE1"] = score1
the_fight["TYPEDATA"]["MATCHES"][m_id]["USERSCORE2"]["SCORE2"] = score2
await self._save_fight(None, t_id, the_fight)
async def _rr_report_dispute(self, guild: discord.Guild, t_id, m_id):
"""Reports a disputed match"""
the_fight = await self._getfight(guild, t_id)
the_fight["TYPEDATA"]["MATCHES"][m_id]["DISPUTE"] = True
await self._save_fight(None, t_id, the_fight)
async def _rr_finalize(self, guild: discord.Guild, t_id):
"""Applies scores to all non-disputed matches"""
the_fight = await self._getfight(guild, t_id)
the_round = the_fight["TYPEDATA"]["SCHEDULE"][the_fight["TYPEDATA"]["ROUND"]]
for m_id in the_round:
if not await self._rr_matchover(guild, t_id, m_id):
match = the_fight["TYPEDATA"]["MATCHES"][m_id]
if (
(match["USERSCORE1"]["SCORE1"] == math.ceil(the_fight["RULES"]["BESTOF"] / 2))
!= (
match["USERSCORE1"]["SCORE2"]
== math.ceil(the_fight["RULES"]["BESTOF"] / 2)
)
and (
match["USERSCORE2"]["SCORE1"]
== math.ceil(the_fight["RULES"]["BESTOF"] / 2)
)
!= (
match["USERSCORE2"]["SCORE2"]
== math.ceil(the_fight["RULES"]["BESTOF"] / 2)
)
and (match["USERSCORE1"]["SCORE1"] == match["USERSCORE2"]["SCORE1"])
and (match["USERSCORE1"]["SCORE2"] == match["USERSCORE2"]["SCORE2"])
):
the_fight["TYPEDATA"]["MATCHES"][m_id]["SCORE1"] = the_fight["TYPEDATA"][
"MATCHES"
][m_id]["USERSCORE1"]["SCORE1"]
the_fight["TYPEDATA"]["MATCHES"][m_id]["SCORE1"] = the_fight["TYPEDATA"][
"MATCHES"
][m_id]["USERSCORE2"]["SCORE2"]
await self._save_fight(None, t_id, the_fight)
else:
await self._rr_report_dispute(guild, t_id, m_id)
async def _rr_parseuser(self, guild: discord.Guild, t_id, userid):
the_fight = await self._getfight(guild, t_id)
matches = the_fight["TYPEDATA"]["MATCHES"]
schedule = the_fight["TYPEDATA"]["SCHEDULE"]
for rnd in schedule:
for mID in rnd:
teamnum = await self._rr_matchperms(guild, t_id, userid, mID)
if teamnum and not await self._rr_matchover(
guild, t_id, mID
): # User is in this match, check if it's done yet
return mID
return False # All matches done or not in tourney
async def _rr_matchover(self, guild: discord.Guild, t_id, m_id):
the_fight = await self._getfight(guild, t_id)
match = the_fight["TYPEDATA"]["MATCHES"][m_id]
if (match["SCORE1"] == math.ceil(the_fight["RULES"]["BESTOF"] / 2)) != (
match["SCORE2"] == math.ceil(the_fight["RULES"]["BESTOF"] / 2)
):
return True
return False
async def _rr_roundover(self, ctx: commands.Context, t_id):
the_fight = await self._getfight(ctx.guild, t_id)
the_round = the_fight["TYPEDATA"]["SCHEDULE"][the_fight["TYPEDATA"]["ROUND"]]
for m_id in the_round:
if not await self._rr_matchover(ctx.guild, t_id, m_id):
return False
return True
async def _rr_matchperms(self, guild: discord.Guild, t_id, userid, m_id):
# if self._get_user_from_id(guildID, userid) # Do an if-admin at start
the_fight = await self._getfight(guild, t_id)
if userid in the_fight["TYPEDATA"]["MATCHES"][m_id]["TEAM1"]:
return 1
if userid in the_fight["TYPEDATA"]["MATCHES"][m_id]["TEAM2"]:
return 2
return False
async def _rr_setup(self, ctx: commands.Context, t_id):
the_fight = await self._getfight(ctx.guild, t_id)
fight_data = the_fight["TYPEDATA"]
get_schedule = self._rr_schedule(the_fight["PLAYERS"])
fight_data["SCHEDULE"] = get_schedule[0]
fight_data["MATCHES"] = get_schedule[1]
fight_data["ROUND"] = 0
await self._save_fight(ctx, t_id, the_fight)
async def _rr_printround(self, ctx: commands.Context, t_id, r_id):
the_fight = await self._getfight(ctx.guild, t_id)
fight_data = the_fight["TYPEDATA"]
channel = await self._get_announcechnnl(ctx.guild)
if channel: # r_id starts at 0, so print +1. Never used for computation, so doesn't matter
await channel.send("**Round " + str(r_id + 1) + " is starting**")
channel = await self._get_reportchnnl(ctx.guild)
for m_id in fight_data["SCHEDULE"][r_id]:
team1 = self._get_team(ctx, fight_data["MATCHES"][m_id]["TEAM1"])
team2 = self._get_team(ctx, fight_data["MATCHES"][m_id]["TEAM2"])
for i in range(len(team1)):
if team1[i]:
team1[i] = team1[i].mention
else:
team1[i] = "BYE"
for i in range(len(team2)):
if team2[i]:
team2[i] = team2[i].mention
else:
team2[i] = "BYE"
mention1 = ", ".join(team1)
mention2 = ", ".join(team2)
outembed = discord.Embed(title="Match ID: " + m_id, color=0x0000BF)
outembed.add_field(name="Team 1", value=mention1, inline=False)
outembed.add_field(name="Team 2", value=mention2, inline=False)
outembed.set_footer(
text=(await self._get_win_str())
+ " Report Win || "
+ (await self._get_loss_str())
+ " Report Loss || "
+ (await self._get_dispute_str())
+ " Dispute Result"
)
if channel:
message = await channel.send(embed=outembed)
await self._add_wld(message)
trackmessage = self.default_tracker.copy()
trackmessage["TID"] = t_id
trackmessage["MID"] = m_id
trackmessage["RID"] = r_id
trackmessage["GUILDID"] = ctx.guild.id
await self._save_tracker(ctx, message.id, trackmessage)
# await ctx.send(team1 + " vs " + team2 + " || Match ID: " + match)
async def _rr_start(self, ctx, t_id):
await self._rr_setup(ctx, t_id)
channel = await self._get_announcechnnl(ctx.guild)
if channel:
await channel.send("**Tournament is Starting**")
await self._rr_printround(ctx, t_id, 0)
# async def _rr_score(self, ctx: commands.Context, tID, mID, t1points, t2points):
# def check(m): #Check Message from author
# return m.author == ctx.author and m.channel == ctx.channel
# theT = await self._getfight(ctx.guild, tID)
# theD = theT["TYPEDATA"]
# # if t1points and t2points:
# # theD["MATCHES"][mID]["SCORE1"] = t1points
# # theD["MATCHES"][mID]["SCORE2"] = t2points
# # self.save_data()
# # return
# if not t1points:
# await ctx.send("Entering scores for match ID: " + mID + "\n\n")
# await ctx.send("How many points did TEAM1 get?")
# if await self._rr_matchperms(ctx.guild, tID, ctx.author.id, mID) == 1:
# await ctx.send("*HINT: You are on TEAM1*")
# # answer = await self.bot.wait_for_message(timeout=120, author=author)
# try:
# answer = await self.bot.wait_for('message', check=check, timeout=120)
# except asyncio.TimeoutError:
# await ctx.send("Cancelled due to timeout")
# return
# try:
# t1points = int(answer.content)
# except:
# await ctx.send("That's not a number!")
# return
# if not t2points:
# await ctx.send("How many points did TEAM2 get?")
# if await self._rr_matchperms(ctx.guild, tID, ctx.author.id, mID) == 2:
# await ctx.send("*HINT: You are on TEAM2*")
# # answer = await self.bot.wait_for_message(timeout=120, author=author)
# try:
# answer = await self.bot.wait_for('message', check=check, timeout=120)
# except asyncio.TimeoutError:
# await ctx.send("Cancelled due to timeout")
# return
# try:
# t2points = int(answer.content)
# except:
# await ctx.send("That's not a number!")
# return
# if (t1points == math.ceil(theT["RULES"]["BESTOF"]/2) or
# t2points == math.ceil(theT["RULES"]["BESTOF"]/2)):
# theD["MATCHES"][mID]["SCORE1"] = t1points
# theD["MATCHES"][mID]["SCORE2"] = t2points
# else:
# await ctx.send("Invalid scores, nothing will be updated")
# return
# await self._save_fight(theT)
# await ctx.send("Scores have been saved successfully!")
# # if self._rr_checkround(guildID, tID)
def _rr_schedule(self, inlist):
""" Create a schedule for the teams in the list and return it"""
s = [] # Schedule list
out_id = {} # Matches
first_id = [
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
] # God dammit this could've been a string
if len(inlist) % 2 == 1:
inlist = inlist + ["BYE"]
for i in range(len(inlist)):
mid = int(len(inlist) / 2)
l1 = inlist[:mid]
l2 = inlist[mid:]
l2.reverse()
match_letter = ""
j = i
while j + 1 > 26:
match_letter += first_id[int(j + 1) % 26 - 1]
j = (j + 1) / 26 - 1
match_letter += first_id[int(j + 1) % 26 - 1]
match_letter = match_letter[::-1]
m_id = []
for ix in range(len(l1)):
m_id += [match_letter + str(ix)]
r_players = list(zip(l1, l2))
team_cnt = 0
for ID in m_id:
out_id[ID] = self.default_match.copy()
out_id[ID]["TEAM1"] = [r_players[team_cnt][0]]
out_id[ID]["TEAM2"] = [r_players[team_cnt][1]]
# out_id[ID] = {
# "TEAM1": [r_players[team_cnt][0]],
# "TEAM2": [r_players[team_cnt][1]],
# "SCORE1": 0,
# "SCORE2": 0,
# "USERSCORE1": {"SCORE1": 0, "SCORE2": 0},
# "USERSCORE2": {"SCORE1": 0, "SCORE2": 0}
# }
team_cnt += 1
# List of match ID's is now done
s += [m_id] # Schedule of matches
inlist.insert(1, inlist.pop())
outlist = [[], {}]
outlist[0] = s
outlist[1] = out_id
# outlist[0] is list schedule of matches
# outlist[1] is dict data of matches
return outlist
# **************** Attempt 2, borrow from Squid*******
async def on_raw_reaction_add(
self, emoji: discord.PartialEmoji, message_id: int, channel_id: int, user_id: int
):
"""
Event handler for long term reaction watching.
:param discord.PartialReactionEmoji emoji:
:param int message_id:
:param int channel_id:
:param int user_id:
:return:
"""
tracker = await self.config.srtracker()
if str(message_id) not in tracker:
return
log_channel = self._get_channel_from_id(390927071553126402)
# await log_channel.send("Message ID: "+str(message_id)+" was just reacted to")
tracker = tracker[str(message_id)]
guild = self.bot.get_guild(tracker["GUILDID"])
member = guild.get_member(user_id)
if member.bot:
return
if tracker["MID"] != (await self._parseuser(guild, tracker["TID"], member.id)):
message = await self._get_message_from_id(guild, message_id)
await message.remove_reaction(emoji, member)
return
channel = guild.get_channel(channel_id)
message = await channel.get_message(message_id)
if emoji.is_custom_emoji():
emoji_id = emoji.id
else:
emoji_id = emoji.name
wld = [
(await self.config.win()),
(await self.config.loss()),
(await self.config.dispute()),
]
if emoji_id not in wld: # Not sure if this works # It does
await message.remove_reaction(emoji, member)
return
if emoji_id == wld[0]:
await self._report_win()
await log_channel.send("Message ID: " + str(message_id) + " was reporting a win")
if emoji_id == wld[1]:
await self._report_loss()
await log_channel.send("Message ID: " + str(message_id) + " was reporting a loss")
if emoji_id == wld[2]:
await self._report_dispute(guild, tracker["TID"], tracker["MID"])
await log_channel.send("Message ID: " + str(message_id) + " was reporting a dispute")