class EVENT: # Executes when loaded def __init__(self): self.RUNNING = False self.param = {"TIME_ORDER": 1} # Executes when activated def start(self, SERVER): # Set the parameters self.RUNNING = True self.MESSAGES = [] self.db = Database() self.SERVER = SERVER self.CHANNEL = "" self.ANNOUNCE = "" # Executes when deactivated def end(self): # Reset the parameters self.param = {"TIME_ORDER": 1} self.RUNNING = False # Exclusive to this event, updates the list of TWOWs in signups async def update_list(self, hour=False, announce=True, update_channel=False): if len(self.MESSAGES) == 0 or update_channel: msgs = [ int(x) for x in self.db.get_entries("signupmessages")[0][0].split(" ") ] self.CHANNEL = discord.utils.get(self.SERVER["MAIN"].channels, id=msgs[0]) self.MESSAGES = [""] * (len(msgs) - 2) self.ANNOUNCE = "" async for msg in self.CHANNEL.history(limit=100): if msg.id in msgs: if msgs.index(msg.id) != len(msgs) - 1: self.MESSAGES[msgs.index(msg.id) - 1] = msg else: self.ANNOUNCE = msg twow_list = self.db.get_entries("signuptwows") twow_list = sorted(twow_list, key=lambda m: self.param["TIME_ORDER"] * m[4]) for ind, twow in enumerate(twow_list): if twow[4] <= time.time(): twow_list[ind] = "" twow_list = [x for x in twow_list if x != ""] self.db.remove_entry("signuptwows") for twow in twow_list: self.db.add_entry("signuptwows", list(twow)) if announce: try: new_twow_names = list(zip(*twow_list))[0] except IndexError: new_twow_names = [] old_twow_names = [ x.content[x.content.find("π **__") + 7:x.content.find("__** - Hosted by")] for x in self.MESSAGES if x.content != "\u200b" ] just_added = [x for x in new_twow_names if x not in old_twow_names] just_removed = [ x for x in old_twow_names if x not in new_twow_names ] new_announcement_list = [] for x in just_added: new_announcement_list.append( f"`(<1 hour ago)` : Added **{x}** to the signup list") for x in just_removed: new_announcement_list.append( f"`(<1 hour ago)` : Removed **{x}** from the signup list") if self.ANNOUNCE.content != "\u200b": old_announcement_list = self.ANNOUNCE.content.split("\n")[2:] if hour: for z in range(len(old_announcement_list)): halves = old_announcement_list[z].split(" : ") halves[0] = halves[0].split(" ") if halves[0][0][2:] == "<1": halves[0] = "`(1 hour ago)`" old_announcement_list[z] = " : ".join(halves) elif halves[0][0][2:] != "23": halves[ 0] = f"`({int(halves[0][0][2:])+1} hours ago)`" old_announcement_list[z] = " : ".join(halves) else: old_announcement_list[z] = "" old_announcement_list = [ x for x in old_announcement_list if x != "" ] if new_announcement_list != []: old_announcement_list += new_announcement_list announce_msg = f"__**Recent list changes:**__\n\n" + "\n".join( old_announcement_list) await self.ANNOUNCE.edit(content=announce_msg) else: announce_msg = f"__**Recent list changes:**__\n\n" + "\n".join( new_announcement_list) await self.ANNOUNCE.edit(content=announce_msg) for x in just_added: verif = twow_list[new_twow_names.index(x)][-1] if verif == 1: msg = await self.CHANNEL.send( "<@&488451010319220766> <@&723946317839073370>") else: msg = await self.CHANNEL.send("<@&723946317839073370>") await msg.delete() formatted_list = [] for twow in twow_list: time_left = twow[4] - time.time() signup_warning = "" time_emoji = "ππππππππππππ" if time_left <= 0: t_l_string = "SIGNUPS ARE OVER!" else: abs_delta = [ np.ceil(time_left / 3600), # Hours int(np.ceil(time_left / 3600) / 24) ] # Days hr = int(abs_delta[0] % 24) dy = int(abs_delta[1]) t_l_string = f"Less than" if dy != 0: t_l_string += f" {dy} day{'s' if dy!=1 else ''}" else: signup_warning = "\nβ° **SIGNUPS ARE ALMOST OVER! JOIN SOON!**" if hr != 0: if dy != 0: t_l_string += "," t_l_string += f" {hr} hour{'s' if hr!=1 else ''}" datetime_dl = datetime.datetime.utcfromtimestamp(twow[4]) deadline_string = datetime_dl.strftime("%B %d %Y %H:%M UTC") try: chosen_emoji = time_emoji[datetime_dl.hour % 12] except Exception: chosen_emoji = time_emoji[0] verified_string = "" if twow[5] > 0: verified_string = "\nβ **FEATURED TWOW!** (<@&488451010319220766>)" descrip = twow[3].replace('\n', '\n> ') message = f"""\u200b \u200b{verified_string} π **__{twow[0]}__** - Hosted by **{twow[1]}** > {descrip} {signup_warning} {chosen_emoji} **Signup Deadline** : **{t_l_string}** `({deadline_string})` π₯ **Server Link** : {twow[2]}""".replace("\t", "") formatted_list.append(message) for t in range(len(self.MESSAGES)): if t < len(formatted_list): await self.MESSAGES[-t - 1].edit(content=formatted_list[t]) elif self.MESSAGES[-t - 1].content != "\u200b": await self.MESSAGES[-t - 1].edit(content="\u200b") # Function that runs every hour async def on_one_hour(self): await self.update_list(hour=True) # Change a parameter of the event async def edit_event(self, message, new_params): incorrect = [] correct = [] for parameter in new_params.keys(): try: self.param[parameter] = new_params[parameter] correct.append(parameter) except KeyError: incorrect.append(parameter) if len(correct) > 0: await message.channel.send( f"Successfully changed the parameters: {grammar_list(correct)}" ) if len(incorrect) > 0: await message.channel.send( f"The following parameters are invalid: {grammar_list(incorrect)}" ) return
async def MAIN(message, args, level, perms): db = Database() if perms < 2: # Direct database viewing is staff-only await message.channel.send("You don't have permission to run this subcommand.") return if level == 2: # If it's just `tc/db main`, list the tables using the get_tables() function. table_list = db.get_tables() table_list = [f'**{table.upper()}**' for table in table_list] await message.channel.send(f"Here's a list of Brain Database's tables: {grammar_list(table_list)}") return if args[1].lower() == "add": # `tc/db main add` creates a table if level == 2: await message.channel.send("Include a name for the new table!") return if level == 3: await message.channel.send("Include the table's columns!") return # I expect columns to be in the format `name-datatype name-datatype` and so on for each column. name = args[2].lower() columns = [x.lower().split("-") for x in args[3:]] db.add_table(name, columns) # Turn the `name-datatype` into `name datatype`, just for readability in the confirmation message columns = [" ".join(x) for x in columns] await message.channel.send(f"Table **{name.upper()}** created with columns {', '.join(columns)}") return if args[1].lower() == "remove": # `tc/db main remove` deletes (a.k.a. drops) a table if level == 2: await message.channel.send("Include the table you want to remove!") return name = args[2].lower() db.remove_table(name) await message.channel.send(f"Successfully deleted table **{name.upper()}**.") return if args[1].lower() == "layout": # `tc/db main layout` displays all columns and datatypes of a table if level == 2: await message.channel.send("Include the table you want to see the layout of!") return name = args[2].lower() columns = db.get_columns(name, include_type=True) # Make them bold, in the format `name - datatype` and with arrows formatted = "\n> ".join([f"**{r}**" for r in [" - ".join(c) for c in columns]]) await message.channel.send( f"Here are the colums and datatypes from the table **{name.upper()}**.\n> {formatted}") return if args[1].lower() == "entries": # `tc/db main entries` Displays, adds or removes entries of a table if level == 2: await message.channel.send("Include the table you want to see the layout of!") return name = args[2].lower() get_entries = False # args[4] is the number of entries you want to see if level == 3: # If it's not specified, assume 10 limit = 10 get_entries = True elif args[3].lower() == "all": # If it's "all", just go with 99999. Better safe than sorry limit = 99999 get_entries = True elif args[3].lower() not in ["add", "remove", "edit"]: # If it's not a further subcommand, interpret it as a number try: limit = int(args[3]) except: # If it's not actually a number, just go with 10 limit = 10 get_entries = True # If get_entries is switched to true, that means the section above was able to extrapolate a number # from the command - meaning the user wants to see the table's entries, not add or remove them if get_entries: entries = db.get_entries(name, limit=limit) # "\t".join([str(h) for h in e]) returns a single entry, with all its columns joined by tabs # ["\t".join([str(h) for h in e]) for e in entries] returns a list of those entry strings # `formatted` is set to all those entries together, separated by newlines and in arrow text formatted = "\n> ".join(["\t".join([str(h) for h in e]) for e in entries]) # Gotta have correct grammar reported_limit = 'all' if limit >= 99999 else limit plural = 'ies' if limit != 1 else 'y' to_send = [f"Here are {reported_limit} entr{plural} of **{name.upper()}**.\n> {formatted}"] # Limit messages to 1950 characters at most. Cut them off if bigger. Getting any closer to 2000 # can cause errors regardless for some reason, so I try to avoid it if len(to_send[0]) > 1950: to_send.append(f"> {to_send[0][1947:3900]}") to_send[0] = to_send[0][:1947] + "..." for z in to_send: await message.channel.send(z) return if args[3].lower() == "add": # I expect arguments for this to be `value1 // value2 // value3` and so on arguments = " ".join(args[4:]).split(" // ") db.add_entry(name, arguments) await message.channel.send(f"Successfully added entry to table **{name.upper()}**!") return if args[3].lower() == "remove": if level == 4: await message.channel.send("Include a search column!") return if level == 5 and args[4].lower() == "all": # Delete all entries in the table await message.channel.send(f"""Are you sure you want to delete all entries in **{name.upper()}**? Send `confirm` to transfer.""".replace("\n", "").replace("\t", "")) # Check for the next message by the same person in the same channel msg = await BRAIN.wait_for('message', check=lambda m: (m.author == message.author and m.channel == message.channel)) if msg.content.lower() != "confirm": # If it's not `confirm`, cancel the command await message.channel.send("Database command cancelled.") return db.remove_entry(name) await message.channel.send(f"Successfully cleared all entries from table **{name.upper()}**!") return # Arguments for this should be `column // value`, as a key to determine what should be deleted arguments = " ".join(args[4:]).split(" // ") if len(arguments) % 2 == 1: # Odd number of arguments means one key has no value await message.channel.send("Include a value for every updating column!") return conditions = {} for z in range(int(len(arguments) / 2)): conditions[arguments[z*2]] = arguments[z*2+1] db.remove_entry(name, conditions) await message.channel.send(f"Successfully deleted entries from table **{name.upper()}**!") return if args[3].lower() == "edit": if level < 7: # Requires at least 3 extra args: `upd_column // upd_key` await message.channel.send("Make sure to include the columns to update!") return if len(" ".join(args[4:]).split(" -> ")) == 2: # Split arguments into searching and updating searching_arguments = " ".join(args[4:]).split(" -> ")[0].split(" // ") updating_arguments = " ".join(args[4:]).split(" -> ")[1].split(" // ") else: searching_arguments = [] updating_arguments = " ".join(args[4:]).split(" // ") if len(searching_arguments) % 2 == 1: await message.channel.send("Include a value for every search column!") return if len(updating_arguments) % 2 == 1: await message.channel.send("Include a value for every updating column!") return conditions = {} for z in range(int(len(searching_arguments) / 2)): conditions[searching_arguments[z*2]] = searching_arguments[z*2+1] entry = {} for z in range(int(len(updating_arguments) / 2)): entry[updating_arguments[z*2]] = updating_arguments[z*2+1] db.edit_entry(name, entry=entry, conditions=conditions) await message.channel.send(f"Successfully edited entries in **{name.upper()}**!") return
async def MAIN(message, args, level, perms, TWOW_CENTRAL): db = Database() months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] if level == 1: # If just tc/bd, return info on their birthday found = db.get_entries("birthday", conditions={"id": str(message.author.id)}) if found == []: await message.channel.send( f"""You are yet to register your birthday! You can register by using **{PREFIX}birthday register `DD/MM` `timezone`**""" .replace("\t", "")) return birthday, tz = found[0][1:] birthday = birthday.split("/") birthday_format = months[int(birthday[1]) - 1] + " " + str(birthday[0]) timezone_f = ("+" if tz > 0 else "") + str(tz) await message.channel.send( f"""**{message.author.name}**'s birthday is set as **{birthday_format}** in **UTC {timezone_f}**.""" ) return if args[1].lower() == "view": if level == 2: found = db.get_entries("birthday") found = sorted(found, key=lambda k: int(k[1].split("/")[0])) found = sorted(found, key=lambda k: int(k[1].split("/")[1])) day, month = datetime.now(timezone.utc).day, datetime.now( timezone.utc).month for bd in found: if int(bd[1].split("/")[1]) > month: next_bd = bd break elif int(bd[1].split("/")[1]) == month and int( bd[1].split("/")[0]) > day: next_bd = bd break else: next_bd = found[0] next_id, birthday, tz = next_bd birthday = birthday.split("/") birthday_format = months[int(birthday[1]) - 1] + " " + str( int(birthday[0])) timezone_f = ("+" if tz > 0 else "") + str(tz) try: username = TWOW_CENTRAL.get_member(int(next_id)).name except AttributeError: username = next_id await message.channel.send( f"The next birthday is **{username}**'s, on **{birthday_format}** in **UTC {timezone_f}**." ) return rest = " ".join(args[2:]) if rest.startswith("<@") and rest.endswith(">"): rest = rest[2:-1] if is_whole(rest): found = db.get_entries("birthday", conditions={"id": rest}) try: username = TWOW_CENTRAL.get_member(int(rest)).name except: username = rest if found == []: await message.channel.send( f"**{username}** has not registered a birthday yet!") return user_bd = found[0] birthday, tz = user_bd[1:] birthday = birthday.split("/") birthday_format = months[int(birthday[1]) - 1] + " " + str( int(birthday[0])) timezone_f = ("+" if tz > 0 else "") + str(tz) await message.channel.send( f"**{username}**'s birthday is on **{birthday_format}** in **UTC {timezone_f}**." ) return else: user = discord.utils.get(TWOW_CENTRAL.members, name=rest) if user is None: await message.channel.send("That user is not in the server!") return found = db.get_entries("birthday", conditions={"id": str(user.id)}) if found == []: await message.channel.send( f"**{rest}** has not registered a birthday yet!") return user_bd = found[0] birthday, tz = user_bd[1:] birthday = birthday.split("/") birthday_format = months[int(birthday[1]) - 1] + " " + str( int(birthday[0])) timezone_f = ("+" if tz > 0 else "") + str(tz) await message.channel.send( f"**{rest}**'s birthday is on **{birthday_format}** in **UTC {timezone_f}**." ) return if args[1].lower() == "register": if level == 2: await message.channel.send( "Include your birthday in `DD/MM` to register!") return # Check if the person is in the birthday database or not found = db.get_entries("birthday", conditions={"id": str(message.author.id)}) print(found) birthday = args[2].split("/") if level == 3: tz = 0 elif not is_whole(args[3]): await message.channel.send( "Invalid timezone! Make sure it's a whole number from -12 to 14!" ) return elif not -12 <= int(args[3]) <= 14: await message.channel.send( "Invalid timezone! Make sure it's a whole number from -12 to 14!" ) return else: tz = int(args[3]) if len(birthday) != 2: # If it's not `n/n` await message.channel.send( "Invalid birthday! Make sure it's in the `DD/MM` format!") return if not is_whole(birthday[0]) or not is_whole( birthday[1]): # If day and month aren't numbers await message.channel.send( "Invalid birthday! Make sure the day and month are both numbers!" ) return # Transform into integers for these next two checks birthday[0] = int(birthday[0]) birthday[1] = int(birthday[1]) if not 1 <= birthday[1] <= 12: # If month is invalid await message.channel.send( "Invalid month! Make sure it's between 1 and 12.") return if not 1 <= birthday[0] <= monthrange( 2020, birthday[1])[1]: # monthrange checks days in the month await message.channel.send( # 2020 months because it's a leap year, and 29/02 should be available f"Invalid day! Make sure it's between 1 and {monthrange(2020, birthday[1])[1]} for that month." ) return birthday_format = months[birthday[1] - 1] + " " + str(birthday[0]) birthday = "/".join([str(x) for x in birthday ]) # Join the list again for the next few lines timezone_f = ("+" if tz > 0 else "") + str(tz) # This confirmation message cannot be bypassed await message.channel.send( f"""Are you sure you want to record your birthday as {birthday_format} and your timezone as UTC {timezone_f}? Send `confirm` in this channel to confirm. """.replace("\n", "").replace("\t", "")) # Wait for a message by the same author in the same channel msg = await BRAIN.wait_for( 'message', check=(lambda m: m.channel == message.channel and m.author == message.author)) if msg.content.lower( ) != "confirm": # If it's not `confirm`, cancel command await message.channel.send("Birthday registering cancelled.") return # If confirmation passed, record the birthday if found == []: is_new = "" db.add_entry("birthday", [message.author.id, birthday, tz]) else: is_new = "new " db.edit_entry("birthday", entry={ "birthday": birthday, "timezone": tz }, conditions={"id": str(message.author.id)}) await message.channel.send( f"Successfully recorded your {is_new}birthday as **{birthday} UTC {timezone_f}**!" ) return
async def MAIN(message, args, level, perms, SERVER): if level == 1: await message.channel.send("Include a subcommand!") return db = Database() if args[1].lower() == "info": tag_list = db.get_entries("b++2programs", columns=["name", "program", "author", "uses", "created", "lastused"]) tag_list = sorted(tag_list, reverse=True, key=lambda m: m[3]) tag_leaderboard = False if level == 2: # If it's not specified, assume it's the first page tag_list = tag_list[:10] page = 1 tag_leaderboard = True elif is_whole(args[2]): if (int(args[2]) - 1) * 10 >= len(tag_list): # Detect if the page number is too big await message.channel.send(f"There is no page {args[2]} on the New B++ program list!") return else: # This means the user specified a valid page number lower = (int(args[2]) - 1) * 10 upper = int(args[2]) * 10 tag_list = tag_list[lower:upper] page = int(args[2]) tag_leaderboard = True if tag_leaderboard: beginning = f"```diff\nB++ Programs Page {page}\n\n" for program in tag_list: r = tag_list.index(program) + 1 + (page - 1) * 10 line = f"{r}{' '*(2-len(str(r)))}: {program[0]} :: {program[3]} use{'s' if program[3] != 1 else ''}" member_id = program[2] try: # Try to gather a username from the ID member = SERVER["MAIN"].get_member(int(member_id)).name except: # If you can't, just display the ID member = str(member_id) created_on = dt.utcfromtimestamp(program[4]).strftime('%Y-%m-%d %H:%M:%S UTC') line += f" (written by {member} at {created_on})\n" beginning += line # Add this line to the final message beginning += "```" # Close off code block await message.channel.send(beginning) return tag_name = args[2] if tag_name not in [x[0] for x in tag_list]: await message.channel.send("That tag does not exist.") return program = tag_list[[x[0] for x in tag_list].index(tag_name)] member_id = program[2] try: # Try to gather a username from the ID member = SERVER["MAIN"].get_member(int(member_id)).name except: # If you can't, just display the ID member = str(member_id) created_on = dt.utcfromtimestamp(program[4]).strftime('%Y-%m-%d %H:%M:%S UTC') c_d = dt.now() - dt.utcfromtimestamp(program[4]) d = c_d.days h, rm = divmod(c_d.seconds, 3600) m, s = divmod(rm, 60) c_d = (('' if d==0 else f'{d} day{"s" if d!=1 else ""}, ') + ('' if h==0 else f'{h} hour{"s" if h!=1 else ""}, ') + ('' if m==0 else f'{m} minute{"s" if m!=1 else ""}, ') + (f'{s} second{"s" if s!=1 else ""}')) msg = f"**{program[0]}** -- by {member} -- {program[3]} use{'s' if program[3]!=1 else ''}\n" msg += f"Created on {created_on} `({c_d} ago)`\n" if program[5] != 0: last_used = dt.utcfromtimestamp(program[5]).strftime('%Y-%m-%d %H:%M:%S UTC') u_d = dt.now() - dt.utcfromtimestamp(program[5]) d = u_d.days h, rm = divmod(u_d.seconds, 3600) m, s = divmod(rm, 60) u_d = (('' if d==0 else f'{d} day{"s" if d!=1 else ""}, ') + ('' if h==0 else f'{h} hour{"s" if h!=1 else ""}, ') + ('' if m==0 else f'{m} minute{"s" if m!=1 else ""}, ') + (f'{s} second{"s" if s!=1 else ""}')) msg += f"Last used on {last_used} `({u_d} ago)`\n" if len(program[1]) > 1700: msg += f"The program is too long to be included in the message, so it's in the file below:" open(f'program_{program[0]}.txt', 'w', encoding="utf-8").write(program[1]) await message.channel.send(msg, file=discord.File(f'program_{program[0]}.txt')) os.remove(f'program_{program[0]}.txt') else: msg += f"```{program[1]}```" await message.channel.send(msg) return if args[1].lower() == "create": if level == 2: await message.channel.send("Include the name of your new program!") return tag_name = args[2] if re.search(r"[^0-9A-Za-z_]", tag_name) or re.search(r"[0-9]", tag_name[0]): await message.channel.send( "Tag name can only contain letters, numbers and underscores, and cannot start with a number!") return if tag_name in ["create", "edit", "delete", "info", "run", "help"]: await message.channel.send("The tag name must not be a reserved keyword!") return if len(tag_name) > 30: await message.channel.send("That tag name is too long. 30 characters maximum.") return if level > 3: program = " ".join(args[3:]) elif len(message.attachments) != 0: try: if message.attachments[0].size >= 20000: await message.channel.send("Your program must be under **20KB**.") return await message.attachments[0].save(f"Config/{message.id}.txt") except Exception: await message.channel.send("Include a valid program to save!") return program = open(f"Config/{message.id}.txt", "r", encoding="utf-8").read() os.remove(f"Config/{message.id}.txt") else: await message.channel.send("Include a valid program to save!") return while program.startswith("`") and program.endswith("`"): program = program[1:-1] program.replace("{}", "\t") if (tag_name,) in db.get_entries("b++2programs", columns=["name"]): await message.channel.send("There's already a program with that name!") return db.add_entry("b++2programs", [tag_name, program, message.author.id, 0, time.time(), 0]) await message.channel.send(f"Successfully created program `{tag_name}`!") return if args[1].lower() == "edit": if level == 2: await message.channel.send("Include the name of the program you want to edit!") return tag_name = args[2] tag_list = db.get_entries("b++2programs", columns=["name", "author"]) if tag_name not in [x[0] for x in tag_list]: await message.channel.send(f"There's no program under the name `{tag_name}`!") return ind = [x[0] for x in tag_list].index(tag_name) if tag_list[ind][1] != str(message.author.id) and perms < 2: await message.channel.send(f"You can only edit a program if you created it or if you're a staff member!") return if level > 3: program = " ".join(args[3:]) elif len(message.attachments) != 0: try: if message.attachments[0].size >= 20000: await message.channel.send("Your program must be under **20KB**.") return await message.attachments[0].save(f"Config/{message.id}.txt") except Exception: await message.channel.send("Include a valid program to run!") return program = open(f"Config/{message.id}.txt", "r", encoding="utf-8").read() os.remove(f"Config/{message.id}.txt") else: await message.channel.send("Include a valid program to run!") return while program.startswith("`") and program.endswith("`"): program = program[1:-1] program = program.replace("{}", "\v") db.edit_entry("b++2programs", entry={"program": program}, conditions={"name": tag_name}) await message.channel.send(f"Succesfully edited program {tag_name}!") return if args[1].lower() == "delete": if level == 2: await message.channel.send("Include the name of the program you want to delete!") return tag_name = args[2] tag_list = db.get_entries("b++2programs", columns=["name", "author"]) if tag_name not in [x[0] for x in tag_list]: await message.channel.send(f"There's no program under the name `{tag_name}`!") return ind = [x[0] for x in tag_list].index(tag_name) if tag_list[ind][1] != str(message.author.id) and perms < 2: await message.channel.send(f"You can only edit a program if you created it or if you're a staff member!") return db.remove_entry("b++2programs", conditions={"name": tag_name}) await message.channel.send(f"Succesfully deleted program {tag_name}!") return if args[1].lower() == "run": if level > 2: program = " ".join(args[2:]) elif len(message.attachments) != 0: try: if message.attachments[0].size >= 20000: await message.channel.send("Your program must be under **20KB**.") return await message.attachments[0].save(f"Config/{message.id}.txt") except Exception: await message.channel.send("Include a valid program to run!") return program = open(f"Config/{message.id}.txt", "r", encoding="utf-8").read() os.remove(f"Config/{message.id}.txt") else: await message.channel.send("Include a valid program to run!") return while program.startswith("`") and program.endswith("`"): program = program[1:-1] program = program.replace("{}", "\v") program_args = [] author = message.author.id else: tag_name = args[1] tag_list = db.get_entries("b++2programs", columns=["name", "program", "author", "uses"]) if tag_name not in [x[0] for x in tag_list]: await message.channel.send(f"There's no program under the name `{tag_name}`!") return tag_info = [x for x in tag_list if x[0] == tag_name][0] program = tag_info[1] uses = tag_info[3] + 1 db.edit_entry("b++2programs", entry={"uses": uses, "lastused": time.time()}, conditions={"name": tag_name}) program_args = args[2:] author = tag_info[2] try: program_output = run_bpp_program(program, program_args, author) program_output = program_output.replace("<@", "<\\@") except Exception as e: await message.channel.send(f'{type(e).__name__}:\n```{e}```') return if len(program_output) > 1950: program_output = "β οΈ `Output too long! First 1900 characters:`\n\n" + program_output[:1900] if len(program_output.strip()) == 0: program_output = "\u200b" await message.channel.send(program_output) return
async def MAIN(message, args, level, perms, SERVER): if level == 1: await message.channel.send("Include a subcommand!") return VARIABLES = {} db = Database() if args[1].lower() == "info": tag_list = db.get_entries("b++programs", columns=["name", "program", "author", "uses"]) tag_list = sorted(tag_list, reverse=True, key=lambda m: m[3]) tag_leaderboard = False if level == 2: # If it's not specified, assume it's the first page tag_list = tag_list[:10] page = 1 tag_leaderboard = True elif is_whole(args[2]): if (int(args[2]) - 1) * 10 >= len(tag_list): # Detect if the page number is too big await message.channel.send(f"There is no page {args[2]} on the B++ program list!") return else: # This means the user specified a valid page number lower = (int(args[2]) - 1) * 10 upper = int(args[2]) * 10 tag_list = tag_list[lower:upper] page = int(args[2]) tag_leaderboard = True if tag_leaderboard: beginning = f"```diff\nOld B++ Programs Page {page}\n\n" for program in tag_list: r = tag_list.index(program) + 1 + (page - 1) * 10 line = f"{r}{' '*(2-len(str(r)))}: {program[0]} :: {program[3]} use{'s' if program[3] != 1 else ''}" member_id = program[2] try: # Try to gather a username from the ID member = SERVER["MAIN"].get_member(int(member_id)).name except: # If you can't, just display the ID member = str(member_id) line += f" (written by {member})\n" beginning += line # Add this line to the final message beginning += "```" # Close off code block await message.channel.send(beginning) return tag_name = args[2] if tag_name not in [x[0] for x in tag_list]: await message.channel.send("That tag does not exist.") return program = tag_list[[x[0] for x in tag_list].index(tag_name)] member_id = program[2] try: # Try to gather a username from the ID member = SERVER["MAIN"].get_member(int(member_id)).name except: # If you can't, just display the ID member = str(member_id) if len(program[1]) + 6 >= 1900: program_code_msg = program[1] if len(program_code_msg) >= 1990: await message.channel.send(f"**{program[0]}** (written by {member})") await message.channel.send(f"```{program_code_msg[:1000]}```") await message.channel.send(f"```{program_code_msg[1000:]}```") await message.channel.send(f"Used {program[3]} time{'s' if program[3] != 1 else ''}") else: await message.channel.send(f"**{program[0]}** (written by {member})") await message.channel.send(f"```{program_code_msg}```") await message.channel.send(f"Used {program[3]} time{'s' if program[3] != 1 else ''}") else: await message.channel.send(f"""**{program[0]}** (written by {member}) ```{program[1]}``` Used {program[3]} time{'s' if program[3] != 1 else ''}""".replace("\n", "").replace("\t", "")) return if args[1].lower() == "delete": if level == 2: await message.channel.send("Include the name of the program you want to delete!") return tag_name = args[2] tag_list = db.get_entries("b++programs", columns=["name", "author"]) if tag_name in [x[0] for x in tag_list]: ind = [x[0] for x in tag_list].index(tag_name) if tag_list[ind][1] != str(message.author.id) and perms != 2: await message.channel.send(f"You can only delete a program you created!") return db.remove_entry("b++programs", conditions={"name": tag_name}) await message.channel.send(f"Succesfully deleted program {tag_name}!") else: await message.channel.send(f"There's no program under the name `{tag_name}`!") return if args[1].lower() == "edit": if level == 2: await message.channel.send("Include the name of the program you want to edit!") return if level == 3: await message.channel.send("Include the new code for the program you want to edit!") return tag_name = args[2] program = " ".join(args[3:]) if program.startswith("```") and program.endswith("```"): program = program[3:-3] if program.startswith("``") and program.endswith("``"): program = program[2:-2] if program.startswith("`") and program.endswith("`"): program = program[1:-1] tag_list = db.get_entries("b++programs", columns=["name", "author"]) if tag_name in [x[0] for x in tag_list]: ind = [x[0] for x in tag_list].index(tag_name) if tag_list[ind][1] != str(message.author.id) and perms != 2: await message.channel.send(f"You can only edit a program you created!") return db.edit_entry("b++programs", entry={"program": program}, conditions={"name": tag_name}) await message.channel.send(f"Succesfully edited program {tag_name}!") else: await message.channel.send(f"There's no program under the name `{tag_name}`!") return if args[1].lower() not in ["run", "create"]: tag_name = args[1] if (tag_name,) in db.get_entries("b++programs", columns=["name"]): program, uses = db.get_entries( "b++programs", columns=["program", "uses"], conditions={"name": tag_name})[0] uses += 1 db.edit_entry("b++programs", entry={"uses": uses}, conditions={"name": tag_name}) else: await message.channel.send(f"There's no tag under the name `{args[1]}`!") return else: if args[1].lower() == "run": if level == 2: await message.channel.send("Include a program to run!") return program = " ".join(args[2:]) if args[1].lower() == "create": if level == 2: await message.channel.send("Include the name of your new program!") return if level == 3: await message.channel.send("Include the code for your program!") return tag_name = args[2] if len(tag_name) > 30: await message.channel.send("That tag name is too long. 30 characters maximum.") return program = " ".join(args[3:]) if program.startswith("```") and program.endswith("```"): program = program[3:-3] if program.startswith("``") and program.endswith("``"): program = program[2:-2] if program.startswith("`") and program.endswith("`"): program = program[1:-1] if args[1].lower() == "create": if (tag_name,) in db.get_entries("b++programs", columns=["name"]): await message.channel.send("There's already a program with that name!") return db.add_entry("b++programs", [tag_name, program, message.author.id, 0]) await message.channel.send(f"Successfully created program `{tag_name}`!") return semicolon_inds = find_all(program, ";") semicolon_inds = [x for x in semicolon_inds if program[x-1] != "\\"] program_chars = list(program) for ind in semicolon_inds: program_chars[ind] = "\n" program = ''.join(program_chars).replace("\t", "") lines = program.split("\n") context = [] OUTPUT = "" try: try: tag_vars = db.get_entries("b++variables", columns=["name", "value"], conditions={"tag": tag_name}) for var in tag_vars: value = var[1] if value.startswith("[") and value.endswith("]"): value = array_to_list(value) VARIABLES[var[0]] = value except: pass for line in lines: c_line = line if len(context) == 0: declaration = " = " in c_line array_context = "[" in c_line.replace("\[", "") and "]" not in c_line.replace("\]", "") if array_context: context.append(["array", c_line, c_line[c_line.find("["):]]) continue else: context_list = [x[0] for x in context] if "array" in context_list: this_context = context[context_list.index("array")] this_context[1] += "\t" + c_line if "]" not in line.replace("\]", ""): this_context[2] += "\t" + c_line continue else: this_context[2] += c_line[:c_line.find("]")+1] c_line = this_context[1] del context[context_list.index("array")] declaration = " = " in c_line if declaration: # Deal with variable declaration c_line = c_line.replace("\\=", "\n") c_line = c_line.replace("==", "\t\t") sides = c_line.split("=") sides[1] = "=".join(sides[1:]) sides = [x.replace("\n", "\=") for x in sides] sides = [x.replace("\t\t", "==") for x in sides] c_line = c_line.replace("\n", "=") c_line = c_line.replace("\t\t", "==") sides[0] = parenthesis_parser(sides[0].strip(), VARIABLES, OUTPUT)[0] sides[1] = parenthesis_parser(strip_front(sides[1]), VARIABLES, OUTPUT, var=True)[0] VARIABLES[sides[0]] = sides[1] continue line_info, OUTPUT = parenthesis_parser(c_line.strip(), VARIABLES, OUTPUT) except Exception as e: await message.channel.send(f'{type(e).__name__} in line `{c_line}`:\n\t{e}') return try: await message.channel.send( OUTPUT.replace("<@", "<\@").replace("\\\\", "\t\t").replace("\\", "").replace("\t\t", "\\").replace(u"\uF000","\n",50)[:1950]) except discord.errors.HTTPException: pass try: tag_name tag_vars = db.get_entries("b++variables", columns=["name"], conditions={"tag": tag_name}) for var in VARIABLES.keys(): if var.startswith("__"): if type(VARIABLES[var]) == list: VARIABLES[var] = list_to_array(VARIABLES[var]) if (var,) in tag_vars: db.edit_entry("b++variables", entry={"value": str(VARIABLES[var])}, conditions={"name": var}) continue db.add_entry("b++variables", entry=[var, str(VARIABLES[var]), tag_name]) except: pass return
async def MAIN(message, args, level, perms, SERVER): if isinstance(message.channel, discord.DMChannel ): # Everyone should be able to see button presses await message.channel.send("This command cannot be used in DMs!") return db = Database() if level == 1: # `tc/button` will check the current button try: # "public.bigredbutton" always has a single entry unless there has never been a button. If len(button_info) # is 0, that's the case, and this will throw an error caught by the try except block. button_info = db.get_entries("bigredbutton", columns=["button", "info"])[0] except IndexError: button_info = db.get_entries("bigredbutton", columns=["button", "info"]) if len(button_info) == 0: # If there is no button, create one button_number = 1 # It'd be the first ever button serial_number = key_generator(random.randrange( 8, 15)) # Generate serial exploding_chance = random.randrange( 15, 51) # 15 to 50% chance of exploding inspector = number_key(3) # Factory inspector code db.add_entry( "bigredbutton", [1, f"{serial_number} {exploding_chance} {inspector}", "", ""]) # Insert the new button info. Now there is a button, so skip to [*] elif button_info[ 1] == "PRESSED": # This is the 3 second interval after a button is pressed and before return # the outcome is announced. Ignore button checks for this period elif len(button_info[1].split(" ")) == 1: # [#] # Button, when in cooldown, has a single code for its info column, representing when it was pressed and # if it blew up or not. This code, unlike the normal `serial exp_chance` information, has no space in # it. This section detects that code pressing_time = int( button_info[1][2:]) # Time at which the button was pressed if button_info[1].startswith( "0-"): # `0-time` means button didn't blow up left = int((pressing_time + 15) - time.time()) # Time left for button to come back if left < 0: # If it's negative, the button should've returned by now but didn't # So generate the button on the spot button_number = button_info[ 0] + 1 # Increment button number serial_number = key_generator(random.randrange( 8, 15)) # Generate serial exploding_chance = random.randrange( 15, 51) # 15 to 50% chance of exploding inspector = number_key(3) # Factory inspector code n_info = f"{serial_number} {exploding_chance} {inspector}" db.edit_entry("bigredbutton", entry={ "button": button_number, "info": n_info }) # Update with the new button else: # If it's not negative, and the timer hasn't ended, report the amount of time remaining await message.channel.send( f"The new button is currently being prepared! {left}s remain!" ) return if button_info[1].startswith( "1-"): # `1-time` means the button blew up left = int((pressing_time + 300) - time.time()) # Time left for button to come back if left < 0: # Read above about the timer being negative button_number = button_info[0] + 1 serial_number = key_generator(random.randrange(8, 15)) exploding_chance = random.randrange(15, 51) inspector = number_key(3) n_info = f"{serial_number} {exploding_chance} {inspector}" db.edit_entry("bigredbutton", entry={ "button": button_number, "info": n_info }) # Update with new button else: # Report the actual timer if it hasn't ended yet mn = int(left / 60) # Extract minutes sc = left % 60 # Extract seconds await message.channel.send( f"The new button is being reconstructed. {mn}min {sc}s remain!" ) return else: # If there's a button and that's it button_number = button_info[0] # Report the button number... serial_number = button_info[1].split(" ")[0] # Its serial... exploding_chance = button_info[1].split(" ")[ 1] # ...Its explosion chance inspector = button_info[1].split(" ")[2] # And its inspector # [*] Report the current button await message.channel.send( f"""<:bigredbutton:654042578617892893> This is **Big Red Button #{button_number}** It has a **{exploding_chance}%** chance of exploding. The serial number is `{serial_number}`. It was inspected and approved by Factory Inspector #{inspector}. Use `tc/bigredbutton press` to press this button!""".replace("\t", "")) return if args[1].lower() == "top": # Points leaderboard unformatted_points = db.get_entries("bigredbutton", columns=["points"])[0] unformatted_points = [ x.split("-") for x in unformatted_points[0].split(" ") ] # unformatted_points might include empty strings from how the points value is formatted points = [] # This variable will be the clean version for x in unformatted_points: try: x[0] = int(x[0]) x[1] = int(x[1]) points.append( x) # If it passed the int tests, append it to points except ValueError: # If it fails to convert to integers, it's an empty string and is ignored continue points = sorted(points, reverse=True, key=lambda x: x[1]) # Sort the leaderboard player_info = [x for x in points if x[0] == message.author.id][0] player_ind = points.index(player_info) # args[2] is the page number. if level == 2: # If it's not specified, assume it's the first page points = points[:10] page = 1 elif not is_whole( args[2] ): # If it's not a valid integer, assume it's first page also points = points[:10] page = 1 elif (int(args[2]) - 1) * 10 >= len(points): # Detect if the page number is too big await message.channel.send( f"There is no page {args[2]} on Big Red Button!") return else: # This means the user specified a valid page number lower = (int(args[2]) - 1) * 10 upper = int(args[2]) * 10 points = points[lower:upper] page = int(args[2]) # Top of the message beginning = f"```diff\n---β Big Red Button Points Leaderboard Page {page} β---\n" beginning += "\n Rank | Name | Points\n" for person in points: r = points.index(person) + 1 + (page - 1) * 10 if r == 1: # + if the person is first line = f"+ {r}{' ' * (4 - len(str(r)))}| " else: # - otherwise line = f"- {r}{' ' * (4 - len(str(r)))}| " try: # Try to gather a username from the ID member = SERVER["MAIN"].get_member(int(person[0])).name except: # If you can't, just display the ID member = str(person[0]) line += f"{member[:20]}{' ' * (22 - len(member[:20]))}| " # Trim usernames to 20 characters long line += str(person[1]) + "\n" # Add points value and newline beginning += line # Add this line to the final message beginning += f"\nYour rank is {player_ind+1}, with {player_info[1]} points." beginning += "```" # Close off code block await message.channel.send(beginning) return if args[1].lower() == "press": # Press the button! button_info = db.get_entries("bigredbutton")[0] # This segment is almost an exact repeat of [#] up above if len(button_info[1].split(" ")) == 1 and button_info[1] != "PRESSED": pressing_time = int(button_info[1][2:]) if button_info[1].startswith("0-"): left = int((pressing_time + 15) - time.time()) if left < 0: button_number = button_info[0] + 1 serial_number = key_generator(random.randrange(8, 15)) exploding_chance = random.randrange(15, 51) inspector = number_key(3) n_info = f"{serial_number} {exploding_chance} {inspector}" db.edit_entry("bigredbutton", entry={ "button": button_number, "info": n_info }) await message.channel.send( f"""Big Red Button #{button_number} has arrived from inspection by Factory Inspector #{inspector}, now with a {exploding_chance}% chance to explode and a serial number of `{serial_number}`!""".replace("\n", "").replace("\t", "")) return else: await message.channel.send( f"The new button is currently being prepared! {left}s remain!" ) return if button_info[1].startswith("1-"): left = int((pressing_time + 300) - time.time()) if left < 0: button_number = button_info[0] + 1 serial_number = key_generator(random.randrange(8, 15)) exploding_chance = random.randrange(15, 51) inspector = number_key(3) n_info = f"{serial_number} {exploding_chance} {inspector}" db.edit_entry("bigredbutton", entry={ "button": button_number, "info": n_info }) await message.channel.send( f"""Big Red Button #{button_number} has arrived from inspection by Factory Inspector #{inspector}, now with a {exploding_chance}% chance to explode and a serial number of `{serial_number}`!""".replace("\n", "").replace("\t", "")) return else: mn = int(left / 60) sc = left % 60 await message.channel.send( f"The new button is being reconstructed. {mn}min {sc}s remain!" ) return # We already checked button_info[1], check two others now button_number = button_info[0] incapacitated = button_info[2] if str(message.author.id) in incapacitated: # If you're incapacitated incapacitated = incapacitated.split(" ") ind = 0 # Find the author in the incapacitated list and extract their explosion time for incap in incapacitated: if incap.split("-")[0] == str(message.author.id): ind = incapacitated.index(incap) explosion_t = int(incap.split("-")[1]) break # Calculate how long it'll be before they can recover delta = (explosion_t + 21600) - time.time() if delta < 0: # If it's negative, then they already recovered and can go on del incapacitated[ind] # Delete the entry for this person incapacitated = " ".join(incapacitated) # Join the list # Update with the new incapacitated list db.edit_entry("bigredbutton", entry={"incapacitated": incapacitated}) else: # If it's not negative, they still have to wait a little abs_delta = [ int(delta), # Seconds int(delta / 60), # Minutes int(delta / (60 * 60)) ] # Hours sc = abs_delta[0] % 60 mn = abs_delta[1] % 60 hr = abs_delta[2] await message.channel.send( f"You are still incapacitated! Wait {hr}h {mn}min {sc}s to press again." ) return if strip_alpha(message.author.name) == "": return # Don't try to cheese it by having no letters if button_info[ 1] == "PRESSED": # If it's currently being pressed, ignore this press return else: # Mark this button as being pressed so nobody else presses it during the 3 second interval db.edit_entry("bigredbutton", entry={"info": "PRESSED"}) # Gather serial_number and exploding_chance for calculations serial_number = button_info[1].split(" ")[0] exploding_chance = int(button_info[1].split(" ")[1]) inspector = list(button_info[1].split(" ")[2]) new_chance = exploding_chance # Clean slate variable if str(message.author.id)[-1] in serial_number: new_chance *= 0.67 # If last digit of ID is in serial... if strip_alpha(message.author.name)[0].upper() in serial_number: new_chance *= 2 # If first letter of username is in serial... point_retention = 0.5 share_count = 0 disc = list(str(message.author.discriminator)) for x in range(len(list(inspector))): if inspector[x] in disc: share_count += 1 disc.remove(inspector[x]) inspector[x] = "-" if share_count == 2: point_retention = 0.75 if share_count == 3: point_retention = 0.9 seed = random.uniform( 0, 100) # Has to be above the explosion chance otherwise it explodes await message.channel.send( f"**{message.author.name}** presses the button, and...") await asyncio.sleep(3) # Suspense! if seed <= new_chance: # If it's under the explosion chance, it blows up n_button_info = f"1-{int(time.time())}" # Remember, `1-time` is the explosion flag new_inc = f" {message.author.id}-{n_button_info[2:]}" # Add this person to the incapacitated list points = button_info[3].split(" ") # Get the points list new_points = 0 ind = -1 # Find the player in the points list and halve their point count for player in points: if player.split("-")[0] == str(message.author.id): ind = points.index(player) new_points = int( int(player.split("-")[1]) * point_retention) points[ind] = f"{message.author.id}-{new_points}" if ind == -1: # If ind is still -1, then the player wasn't found in the points list so create a new points.append(f"{message.author.id}-{new_points}" ) # entry for them with 0 points points = " ".join(points) db.edit_entry("bigredbutton", entry={ "info": n_button_info, "points": points, "incapacitated": incapacitated + new_inc }) # Update with the explosion info, the new incapacitated list, and the new points list await message.channel.send( f"""<:bigredbutton:654042578617892893> ***The #{button_number} Big Red Button blew up!*** <@{message.author.id}> has been incapacitated. Their point total is now **{new_points}**. They cannot press any more buttons for 6 hours. The button is broken. It'll take **5 minutes** to rebuild it.""".replace( "\t", "")) await asyncio.sleep(300) # Five minutes until the button is back else: # If seed > new_chance, it doesn't blow up points = button_info[3].split(" ") # Get points list to add points n_button_info = f"0-{int(time.time())}" # `0-time` means no explosion ind = -1 # Find player in points list and add the new points for player in points: if player.split("-")[0] == str(message.author.id): ind = points.index(player) # Note: the points they gain is ALWAYS the nominal value for the exploding chance, not the # modified serial number chance that was used to calculate explosions new_points = int(player.split("-")[1]) + exploding_chance points[ind] = f"{message.author.id}-{new_points}" if ind == -1: # If they're not in the points list, add them with the new point value points.append(f"{message.author.id}-{exploding_chance}") points = " ".join(points) db.edit_entry("bigredbutton", entry={ "info": n_button_info, "points": points }) # Update with the pressing info and the new points list await message.channel.send(f""" <:bigredbutton:654042578617892893> The #{button_number} Big Red Button did nothing. <@{message.author.id}> gained {exploding_chance} points. Another button arrives in **15 seconds**. """.replace("\t", "")) await asyncio.sleep(15) # Fifteen seconds until the button is back # Generate new serial_number and exploding_chance button_number += 1 serial_number = key_generator(random.randrange(8, 15)) exploding_chance = random.randrange(15, 51) inspector = number_key(3) n_info = f"{serial_number} {exploding_chance} {inspector}" db.edit_entry("bigredbutton", entry={ "button": button_number, "info": n_info }) # Update table with the new button # Announce the new button await message.channel.send( f"""Big Red Button #{button_number} has arrived from inspection by Factory Inspector #{inspector}, now with a {exploding_chance}% chance to explode and a serial number of `{serial_number}`!""".replace("\n", "").replace("\t", "")) return
async def MAIN(message, args, level, perms, SERVER): db = Database() months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ] if level == 1: # If just tc/bd, return info on their birthday found = db.get_entries("birthday", conditions={"id": str(message.author.id)}) if found == []: await message.channel.send( f"""You are yet to register your birthday! You can register by using **{SERVER["PREFIX"]}birthday register `DD/MM` `timezone`**""" .replace("\t", "")) return birthday, tz = found[0][1:] birthday = birthday.split("/") birthday_format = months[int(birthday[1]) - 1] + " " + str(birthday[0]) timezone_f = ("+" if tz > 0 else "") + str(tz) await message.channel.send( f"""**{message.author.name}**'s birthday is set as **{birthday_format}** in **UTC {timezone_f}**.""" ) return if args[1].lower() == "month": if level == 2: chosen_month = datetime.now(timezone.utc).month elif is_whole(args[2]): if int(args[2]) > 12 or int(args[2]) < 1: await message.channel.send( "Invalid month number. If you're picking a month number, choose one between 1 and 12!" ) return chosen_month = int(args[2]) else: possible_months = [ x for x in months if x.lower().startswith(args[2].lower()) ] if len(possible_months) == 0: new_possible = [ x for x in months if args[2].lower() in x.lower() ] if len(new_possible) == 0: await message.channel.send( "There's no valid month with that name!") return if len(new_possible) > 1: await message.channel.send( f"""There are multiple months fitting that search key. Please specify which one you mean! `({', '.join(new_possible)})`""".replace("\t", "")) return possible_months = new_possible if len(possible_months) > 1: await message.channel.send( f"""There are multiple months fitting that search key. Please specify which one you mean! `({', '.join(possible_months)})`""".replace("\t", "")) return chosen_month = months.index(possible_months[0]) + 1 found = db.get_entries("birthday") found = [k for k in found if int(k[1].split("/")[1]) == chosen_month] month_name = months[chosen_month - 1] if len(found) == 0: await message.channel.send( f"There are no registered birthdays in {month_name}!") return found = sorted(found, key=lambda k: int(k[1].split("/")[0])) messages = [ f"Here are all the birthdays registered in {month_name}:\n\n" ] for bd in found: try: username = f"**{SERVER['MAIN'].get_member(int(bd[0])).name}**" except Exception: username = f"**`[{bd[0]}]`**" day = bd[1].split("/")[0] tz = ("+" if bd[2] > 0 else "") + str(bd[2]) line = f"> {username} - {month_name} {day}, UTC {tz}\n" if len(messages[-1]) + len(line) > 1900: messages.append("") messages[-1] += line for m in messages: await message.channel.send(m) return if args[1].lower() == "next": found = db.get_entries("birthday") found = sorted(found, key=lambda k: int(k[1].split("/")[0])) found = sorted(found, key=lambda k: int(k[1].split("/")[1])) day, month = datetime.now(timezone.utc).day, datetime.now( timezone.utc).month for bd in found: if int(bd[1].split("/")[1]) > month: next_bd = bd break elif int(bd[1].split("/")[1]) == month and int( bd[1].split("/")[0]) > day: next_bd = bd break else: next_bd = found[0] next_id, birthday, tz = next_bd birthday = birthday.split("/") birthday_format = months[int(birthday[1]) - 1] + " " + str( int(birthday[0])) timezone_f = ("+" if tz > 0 else "") + str(tz) try: username = SERVER["MAIN"].get_member(int(next_id)).name except AttributeError: username = next_id await message.channel.send( f"The next birthday is **{username}**'s, on **{birthday_format}** in **UTC {timezone_f}**." ) return if args[1].lower() == "user": if level == 2: await message.channel.send( "Include the username or ID of the person whose birthday you want to check." ) return rest = " ".join(args[2:]) if rest.startswith("<@") and rest.endswith(">"): rest = rest[2:-1] if is_whole(rest): found = db.get_entries("birthday", conditions={"id": rest}) try: username = SERVER["MAIN"].get_member(int(rest)).name except: username = rest if found == []: await message.channel.send( f"**{username}** has not registered a birthday yet!") return user_bd = found[0] birthday, tz = user_bd[1:] birthday = birthday.split("/") birthday_format = months[int(birthday[1]) - 1] + " " + str( int(birthday[0])) timezone_f = ("+" if tz > 0 else "") + str(tz) await message.channel.send( f"**{username}**'s birthday is on **{birthday_format}** in **UTC {timezone_f}**." ) return else: user = discord.utils.get(SERVER["MAIN"].members, name=rest) if user is None: await message.channel.send("That user is not in the server!") return found = db.get_entries("birthday", conditions={"id": str(user.id)}) if found == []: await message.channel.send( f"**{rest}** has not registered a birthday yet!") return user_bd = found[0] birthday, tz = user_bd[1:] birthday = birthday.split("/") birthday_format = months[int(birthday[1]) - 1] + " " + str( int(birthday[0])) timezone_f = ("+" if tz > 0 else "") + str(tz) await message.channel.send( f"**{rest}**'s birthday is on **{birthday_format}** in **UTC {timezone_f}**." ) return if args[1].lower() == "remove": found = db.get_entries("birthday", conditions={"id": str(message.author.id)}) if found == []: await message.channel.send( "You haven't registered a birthday yet to remove it!") return await message.channel.send( f"""Are you sure you want to remove your birthday from the database? Send `confirm` in this channel to confirm. Send anything else to cancel.""".replace( "\n", "").replace("\t", "")) # Wait for a message by the same author in the same channel msg = await BRAIN.wait_for( 'message', check=(lambda m: m.channel == message.channel and m.author == message.author)) if msg.content.lower( ) != "confirm": # If it's not `confirm`, cancel command await message.channel.send("Birthday registering cancelled.") return db.remove_entry("birthday", conditions={"id": str(message.author.id)}) await message.channel.send( "Your birthday has been removed from the database.") return if args[1].lower() == "register": if level == 2: await message.channel.send( "Include your birthday in `DD/MM` to register!") return # Check if the person is in the birthday database or not found = db.get_entries("birthday", conditions={"id": str(message.author.id)}) birthday = args[2].split("/") if level == 3: tz = 0 elif not is_whole(args[3]): await message.channel.send( "Invalid timezone! Make sure it's a whole number from -12 to 14!" ) return elif not -12 <= int(args[3]) <= 14: await message.channel.send( "Invalid timezone! Make sure it's a whole number from -12 to 14!" ) return else: tz = int(args[3]) if len(birthday) != 2: # If it's not `n/n` await message.channel.send( "Invalid birthday! Make sure it's in the `DD/MM` format!") return if not is_whole(birthday[0]) or not is_whole( birthday[1]): # If day and month aren't numbers await message.channel.send( "Invalid birthday! Make sure the day and month are both numbers!" ) return # Transform into integers for these next two checks birthday[0] = int(birthday[0]) birthday[1] = int(birthday[1]) if not 1 <= birthday[1] <= 12: # If month is invalid await message.channel.send( "Invalid month! Make sure it's between 1 and 12.") return if not 1 <= birthday[0] <= monthrange( 2020, birthday[1])[1]: # monthrange checks days in the month await message.channel.send( # 2020 months because it's a leap year, and 29/02 should be available f"Invalid day! Make sure it's between 1 and {monthrange(2020, birthday[1])[1]} for that month." ) return birthday_format = months[birthday[1] - 1] + " " + str(birthday[0]) birthday = "/".join([str(x) for x in birthday ]) # Join the list again for the next few lines timezone_f = ("+" if tz > 0 else "") + str(tz) # This confirmation message cannot be bypassed await message.channel.send( f"""Are you sure you want to record your birthday as {birthday_format} and your timezone as UTC {timezone_f}? Send `confirm` in this channel to confirm. """.replace("\n", "").replace("\t", "")) # Wait for a message by the same author in the same channel msg = await BRAIN.wait_for( 'message', check=(lambda m: m.channel == message.channel and m.author == message.author)) if msg.content.lower( ) != "confirm": # If it's not `confirm`, cancel command await message.channel.send("Birthday registering cancelled.") return # If confirmation passed, record the birthday if found == []: is_new = "" db.add_entry("birthday", [message.author.id, birthday, tz]) else: is_new = "new " db.edit_entry("birthday", entry={ "birthday": birthday, "timezone": tz }, conditions={"id": str(message.author.id)}) await message.channel.send( f"Successfully recorded your {is_new}birthday as **{birthday} UTC {timezone_f}**!" ) return
async def MAIN(message, args, level, perms, SERVER): if level == 1: await message.channel.send("Include a subcommand!") return if args[1].lower() == "setup": if level == 2: n = 10 elif is_whole(args[2]): n = int(args[2]) else: n = 10 msg_ids = [str(message.channel.id)] for _ in range(n): msg = await message.channel.send("\u200b") msg_ids.append(str(msg.id)) await message.channel.send(" ".join(msg_ids)) return if args[1].lower() == "update": await message.channel.send("Updating list...") await SERVER["EVENTS"]["SIGNUPS"].update_list(update_channel=True) await message.channel.send("Updated list!") return if args[1].lower() == "edit": msg = message.content if "name:[" not in msg: await message.channel.send( "Include the name of the TWOW you want to edit!") return db = Database() starting_bound = msg[msg.find("name:[") + 6:] twow_name = starting_bound[:starting_bound.find("]")] twow_list = db.get_entries("signuptwows", conditions={"name": twow_name}) if len(twow_list) == 0: await message.channel.send( f"There's no TWOW named **{twow_name}** in the signup list!") return old_entry = twow_list[0] old_entry = dict( zip(["name", "hosts", "link", "description", "time", "verified"], old_entry)) entry = { "name": None, "hosts": None, "link": None, "description": None, "time": None, "verified": None } cond = {"name": twow_name} if "newname:[" in msg: starting_bound = msg[msg.find("newname:[") + 9:] new_name = starting_bound[:starting_bound.find("]")] entry["name"] = new_name if "host:[" in msg: starting_bound = msg[msg.find("host:[") + 6:] hosts = starting_bound[:starting_bound.find("]")] entry["hosts"] = hosts if "link:[" in msg: starting_bound = msg[msg.find("link:[") + 6:] link = starting_bound[:starting_bound.find("]")].replace( "<", "").replace(">", "") entry["link"] = link if "desc:[" in msg: starting_bound = msg[msg.find("desc:[") + 6:] desc = starting_bound[:starting_bound.find("]")] entry["description"] = desc if "deadline:[" in msg: starting_bound = msg[msg.find("deadline:[") + 10:] dl_string = starting_bound[:starting_bound.find("]")] deadline = datetime.datetime.strptime(dl_string, "%d/%m/%Y %H:%M") deadline = deadline.replace( tzinfo=datetime.timezone.utc).timestamp() entry["time"] = deadline if "verified:[" in msg: starting_bound = msg[msg.find("verified:[") + 10:] verified = starting_bound[:starting_bound.find("]")] if verified in ["0", ""]: vf = 0 else: vf = 1 entry["verified"] = vf entry = {k: d for k, d in entry.items() if d is not None} if len(entry.keys()) == 0: await message.channel.send("You've made no edits to this TWOW!") return db.edit_entry("signuptwows", entry=entry, conditions=cond) announce = "dont_announce" not in msg await SERVER["EVENTS"]["SIGNUPS"].update_list(announce=announce) old_info_string = "" for k, v in old_entry.items(): if v != "": tag = k if k == "hosts": tag = "host" if k == "time": tag = "deadline" if k == "description": tag = "desc" old_info_string += f"{tag}:[{v}] " for k, v in entry.items(): old_entry[k] = v new_info_string = "" for k, v in old_entry.items(): if v != "": tag = k if k == "hosts": tag = "host" if k == "time": tag = "deadline" if k == "description": tag = "desc" if k == "link": v = f"<{v}>" new_info_string += f"{tag}:[{v}] " await message.channel.send( f"""**{cond['name']}** has been edited in the signup list. **Old TWOW Info**: {old_info_string} **New TWOW Info**: {new_info_string}""".replace("\t", "")) if args[1].lower() == "remove": msg = message.content if level == 2: await message.channel.send( "Include the name of the TWOW you want to remove!") return db = Database() if "dont_announce" in msg: twow_name = " ".join(args[2:-1]) else: twow_name = " ".join(args[2:]) twow_list = db.get_entries("signuptwows", conditions={"name": twow_name}) if len(twow_list) == 0: await message.channel.send( f"There's no TWOW named **{twow_name}** in the signup list!") return twow_info = twow_list[0] dl_format = datetime.datetime.utcfromtimestamp( twow_info[4]).strftime("%d/%m/%Y %H:%M") db.remove_entry("signuptwows", conditions={"name": twow_name}) announce = "dont_announce" not in msg await SERVER["EVENTS"]["SIGNUPS"].update_list(announce=announce) await message.channel.send( f"""**{twow_info[0]}** has been removed from the signup list! **TWOW Info**: """.replace("\t", "") + f"""name:[{twow_info[0]}] host:[{twow_info[1]}] link:[{twow_info[2]}] desc:[{twow_info[3]}] deadline:[{dl_format}] {'is_verified' if bool(twow_info[5]) else ''}""".replace("\n", "").replace( "\t", "")) return if args[1].lower() == "add": msg = message.content if "name:[" not in msg: await message.channel.send( "Include the name of the TWOW you want to add!") return db = Database() starting_bound = msg[msg.find("name:[") + 6:] twow_name = starting_bound[:starting_bound.find("]")] entry = [twow_name, "", "", "", 0, 0, ""] if "host:[" in msg: starting_bound = msg[msg.find("host:[") + 6:] hosts = starting_bound[:starting_bound.find("]")] entry[1] = hosts if "link:[" in msg: starting_bound = msg[msg.find("link:[") + 6:] link = starting_bound[:starting_bound.find("]")].replace( "<", "").replace(">", "") entry[2] = link if "desc:[": starting_bound = msg[msg.find("desc:[") + 6:] desc = starting_bound[:starting_bound.find("]")] entry[3] = desc if "deadline:[" in msg: starting_bound = msg[msg.find("deadline:[") + 10:] deadline = starting_bound[:starting_bound.find("]")] entry[6] = deadline deadline = datetime.datetime.strptime(deadline, "%d/%m/%Y %H:%M") deadline = deadline.replace( tzinfo=datetime.timezone.utc).timestamp() entry[4] = deadline vf = 0 if "verified:[" in msg: starting_bound = msg[msg.find("verified:[") + 10:] verified = starting_bound[:starting_bound.find("]")] if verified not in ["0", ""]: vf = 1 entry[5] = vf db.add_entry("signuptwows", entry[:6]) announce = "dont_announce" not in msg await SERVER["EVENTS"]["SIGNUPS"].update_list(announce=announce) await message.channel.send( f"""**{entry[0]}** has been added to the list of TWOWs in signups! **Host:** {entry[1]} **Description:** {entry[3]} **Deadline:** {entry[6]} **Deadline Timestamp:** {entry[4]} **Link:** <{entry[2]}>""".replace("\t", ""))
import discord from Config._db import Database from Config._const import BRAIN db = Database() columns = db.get_entries("serverdata") MAIN_SERVER = {} SERVERS = {} MAIN_SERVER["ID"] = 481509601590771724 MAIN_SERVER["MAIN"] = discord.utils.get(BRAIN.guilds, id=MAIN_SERVER["ID"]) MAIN_SERVER["LOGS"] = discord.utils.get(MAIN_SERVER["MAIN"].channels, id=653677748832698378) MAIN_SERVER["BIRTHDAY"] = discord.utils.get(MAIN_SERVER["MAIN"].roles, id=653630098813222943) MAIN_SERVER["MEMES"] = discord.utils.get(MAIN_SERVER["MAIN"].channels, id=656639194415759371) MAIN_SERVER["INTERESTED"] = discord.utils.get(MAIN_SERVER["MAIN"].roles, id=654072824318918677) ''' MAIN_SERVER["ID"] = 653673010821201920 MAIN_SERVER["MAIN"] = discord.utils.get(BRAIN.guilds, id=MAIN_SERVER["ID"]) MAIN_SERVER["LOGS"] = discord.utils.get(MAIN_SERVER["MAIN"].channels, id=653673395954647095) MAIN_SERVER["BIRTHDAY"] = discord.utils.get(MAIN_SERVER["MAIN"].roles, id=653771648616366103) MAIN_SERVER["MEMES"] = discord.utils.get(MAIN_SERVER["MAIN"].channels, id=656641155869704205) MAIN_SERVER["INTERESTED"] = discord.utils.get(MAIN_SERVER["MAIN"].roles, id=654094896562438154) ''' for server in columns:
async def MAIN(message, args, level, perms, SERVER): db = Database() id_search_key = str(SERVER["MAIN"].id) try: roles, contestants, contnames, emojis, limit = db.get_entries( "teamdata", columns=["roles", "contestants", "contname", "emojis", "teamlimit"], conditions={"server":id_search_key} )[0] except IndexError: await message.channel.send("There are no team flairs in this server!") return flair_list = False count = False if level == 1: flair_list = True elif args[1].lower() in ["teams", "list", "options"]: flair_list = True elif args[1].lower() == "count": flair_list = True count = True if flair_list: role_list = roles.split(" ") if contnames != "": cont_list = contnames.split(" / ") if emojis != "": emoji_list = emojis.split(" ") if count: output = [f"Here's the current team member standings.\n\n"] teams = [] for t in range(len(role_list)): team_info = [] try: team_info.append(emoji_list[t]) except: pass try: team_info.append("**" + cont_list[t] + "** -") except: team_info.append("<@&" + role_list[t] + "> -") try: counter = len(discord.utils.get(SERVER["MAIN"].roles, id=int(role_list[t])).members) except: counter = 0 team_info.append(counter) team_info.append("member" + ('' if counter == 1 else 's') + "\n") teams.append(team_info) teams = sorted(teams, reverse=True, key=lambda m: m[1]) teams = [" ".join([str(a) for a in t]) for t in teams] for t in teams: if len(output[-1]) + len(t) > 1980: output.append("") output[-1] += t else: output = [f"Here's a list of available team flairs.\nYou can pick **{limit}** at most.\n\n"] for t in range(len(role_list)): line = "" try: line += emoji_list[t] + " - " except: pass try: line += "**" + cont_list[t] + "** - " except: pass line += "<@&" + role_list[t] + ">\n" if len(output[-1]) + len(line) > 1980: output.append("") output[-1] += line for m in output: await message.channel.send(m) return if args[1].lower() in ["get", "remove"]: if level == 2: await message.channel.send(f"Specify the team flair you want to {args[1].lower()}!") return cont_list = contnames.split(" / ") cont_id_list = contestants.split(" ") role_list = roles.split(" ") cont_list_l = [x.lower() for x in cont_list] key = " ".join(args[2:]).lower() found_first = False picked = [] found_list = [x for x in cont_list_l if x.startswith(key)] if len(found_list) == 1: ind = cont_list_l.index(found_list[0]) picked = [cont_list[ind], cont_id_list[ind], role_list[ind]] elif len(found_list) > 1: found_first = True if picked == []: role_name_list = [ discord.utils.get(SERVER["MAIN"].roles, id=int(x)).name for x in role_list if discord.utils.get(SERVER["MAIN"].roles, id=int(x)) != None] found_list = [x for x in role_name_list if x.lower().startswith(key)] if len(found_list) == 1: ind = role_name_list.index(found_list[0]) picked = [cont_list[ind], cont_id_list[ind], role_list[ind]] elif len(found_list) > 1: found_first = True if picked == []: if found_first: await message.channel.send( "There seem to be multiple flairs matching that search key. Please specify more!") else: await message.channel.send("I couldn't find a team flair matching that search key!") return author_member = SERVER["MAIN"].get_member(message.author.id) current_flair_list = [str(x.id) for x in author_member.roles if str(x.id) in role_list] if args[1].lower() == "get": if picked[1] == str(message.author.id): await message.channel.send("You can't pick your own team flair!") return if picked[2] in current_flair_list: await message.channel.send("You already have that team flair!") return if len(current_flair_list) >= limit: await message.channel.send( f"You already have {limit} flair{'' if limit==1 else 's'}. You can't get any more!") return team_role = discord.utils.get(SERVER["MAIN"].roles, id=int(picked[2])) await author_member.add_roles(team_role) await message.channel.send(f"You have been added to **{picked[0]}**'s team!") return if args[1].lower() == "remove": if picked[2] not in current_flair_list: await message.channel.send("You don't have that team flair!") team_role = discord.utils.get(SERVER["MAIN"].roles, id=int(picked[2])) await author_member.remove_roles(team_role) await message.channel.send(f"You are no longer on **{picked[0]}**'s team.") return
async def MAIN(message, args, level, perms, TWOW_CENTRAL, EVENT, GAME_CHANNEL): if not isinstance( message.channel, discord.DMChannel) and message.channel.id != GAME_CHANNEL: await message.channel.send( f"MiniMiniTWOW commands can only be used in <#{GAME_CHANNEL}>!") return if level == 1: await message.channel.send("Include a subcommand!") return # Shorten the notation for convenience mmt = EVENT["MMT"] if args[1].lower() == "stats": if level == 2: await message.channel.send( "Include the type of stat you want to see!") return db = Database() # public.mmtstats contains ids, placements in each round, and MMT wins from everyone who's participated # in any MMT rounds so far data = db.get_entries("mmtstats") if len(data) == 0: # Just in case it gets reset one day await message.channel.send("There's no data yet!") return leaderboard = [] if args[2].lower() == "points": for person in data: # `person` is a table entry, (id, placements, wins) try: # Try to find that person's username through TWOW Central member = TWOW_CENTRAL.get_member(int(person[0])).name if member is None: member = person[0] except Exception: # If you can't find them, just use the ID instead member = person[0] # MMT seasons are joined with tabs and MMT rounds are joined with spaces in the database ranks = [x.split(" ") for x in person[1].split("\t")] score = 0 rounds = 0 for twow in ranks: # For each season... for p_round in twow: # For each placement in a round... if p_round.strip() == "": continue numbers = p_round.split( "/") # Get a list of [rank, contestantcount] # Points adds the amount of people you beat each round score += int(numbers[1]) - int(numbers[0]) rounds += 1 # Keep track of the round count too leaderboard.append([member, score, rounds]) elif args[2].lower() == "wins": for person in data: try: member = TWOW_CENTRAL.get_member(int(person[0])).name if member is None: member = person[0] except Exception: member = person[0] leaderboard.append([ member, person[2] ]) # person[2] is MMT season wins, so that's all we need elif args[2].lower() == "roundwins": for person in data: try: member = TWOW_CENTRAL.get_member(int(person[0])).name if member is None: member = person[0] except Exception: member = person[0] ranks = [x.split(" ") for x in person[1].split("\t")] wins = 0 for twow in ranks: for p_round in twow: if p_round.strip() == "": continue numbers = p_round.split("/") if int(numbers[0] ) == 1: # Count up for each `1` placement wins += 1 leaderboard.append( [member, wins]) # wins is the total number of round wins elif args[2].lower() == "nr": for person in data: try: member = TWOW_CENTRAL.get_member(int(person[0])).name if member is None: member = person[0] except Exception: member = person[0] ranks = [x.split(" ") for x in person[1].split("\t")] total = 0 rounds = 0 for twow in ranks: for p_round in twow: if p_round.strip() == "": continue numbers = p_round.split("/") # Add this round's NR to the total total += (int(numbers[1]) - int(numbers[0])) / (int(numbers[1]) - 1) rounds += 1 if rounds == 0: continue # Format those as percentage strings average = "{:.2%}".format(total / rounds) total = "{:.2%}".format(total) leaderboard.append([member, total, average]) else: return player_count = len(leaderboard) if args[2].lower( ) == "points": # Sort by points descending, then rounds ascending leaderboard = sorted(leaderboard, key=lambda c: c[2]) leaderboard = sorted(leaderboard, reverse=True, key=lambda c: c[1]) if args[2].lower( ) == "nr": # Sort by total NR -- remove the percentage and convert to float first leaderboard = sorted(leaderboard, reverse=True, key=lambda c: float(c[1][:-1])) if args[2].lower() in [ "wins", "roundwins" ]: # Just sort by wins ascending. Remove people who have none leaderboard = sorted(leaderboard, reverse=True, key=lambda c: c[1]) leaderboard = [x for x in leaderboard if x[1] != 0] for line in range(len( leaderboard)): # Add the rank to each line of the leaderboard leaderboard[line] = [line + 1] + leaderboard[line] # args[3] is the page number. if level == 3: # If it's not specified, assume it's the first page leaderboard = leaderboard[:10] page = 1 elif not is_whole( args[3] ): # If it's not a valid integer, assume it's first page also leaderboard = leaderboard[:10] page = 1 elif (int(args[3]) - 1) * 10 >= len( leaderboard): # Detect if the page number is too big await message.channel.send( f"There is no page {args[3]} of this stat!") return else: # This means the user specified a valid page number lower = (int(args[3]) - 1) * 10 upper = int(args[3]) * 10 leaderboard = leaderboard[lower:upper] page = args[3] # Headers for each stat if args[2].lower() == "points": final_message = f"```diff\n---β MiniMiniTWOW Point Leaderboard Page {page} β---\n\n" final_message += " Rank | Name | Pts. | Rounds\n" spacing = 6 if args[2].lower() == "nr": final_message = f"```diff\n---β MiniMiniTWOW Normalized Rank Leaderboard Page {page} β---\n\n" final_message += " Rank | Name | Total | Average\n" spacing = 9 if args[2].lower() == "wins": final_message = f"```diff\n---β MiniMiniTWOW Wins Leaderboard Page {page} β---\n\n" final_message += " Rank | Name | Wins\n" spacing = 4 if args[2].lower() == "roundwins": final_message = f"```diff\n---β MiniMiniTWOW Round Wins Leaderboard Page {page} β---\n\n" final_message += " Rank | Name | Round Wins\n" spacing = 5 # Composition of each line of the leaderboard for line in leaderboard: symbol = "+" if line[0] == 1 else "-" spaced_rank = f"{line[0]}{' ' * (4 - len(str(line[0])))}" spaced_name = f"{line[1][:23]}{' '*(24 - len(str(line[1])))}" spaced_points = f"{line[2]}{' '*(spacing - len(str(line[2])))}" try: # Some stats will have two number columns... formatted = f"{symbol} {spaced_rank}| {spaced_name}| {spaced_points}| {line[3]}\n" except: # ...but others will have one. This try except block detects each one. formatted = f"{symbol} {spaced_rank}| {spaced_name}| {spaced_points}\n" final_message += formatted # Add the line to the message final_message += "```" # Close off the code block await message.channel.send(final_message) return if args[1].lower() == "end": if not mmt.RUNNING: await message.channel.send( "There's no MiniMiniTWOW running right now!") return if message.author.id == mmt.info["GAME"][ "HOST"]: # Automatically end MMT if it's the host trying to end it await message.channel.send( "**The current MiniMiniTWOW has been ended by the host! The queue moves on.**" ) mmt.force_skip() return if perms == 2: # Do the same if it's a staff member await message.channel.send( "**The current MiniMiniTWOW has been ended by staff! The queue moves on.**" ) mmt.force_skip() return if mmt.info["GAME"][ "PERIOD"] == 1: # Spectator votes have to wait until post-start, though await message.channel.send( "You can only vote to end a MiniMiniTWOW that has already started!" ) return spect = len(mmt.info["SPECTATORS"]) # Number of spectators necessary = np.ceil( spect**(4 / 5) + 0.8 ) # By the formula, the number of votes necessary to end the MMT # If there are less than 4 spectators, you can't end an MMT by spectator vote. As such, there's not much # reason to bother including the amount of votes necessary if necessary > spect, so include it only if # necessary <= spect instead nec_seg = "" if necessary <= spect: nec_seg = f"/{necessary}" if message.author.id in mmt.info["GAME"]["END_VOTES"]: # Filter the user's ID out of the list of end voters mmt.info["GAME"]["END_VOTES"] = [ x for x in mmt.info["GAME"]["END_VOTES"] if x != message.author.id ] votes = len(mmt.info["GAME"] ["END_VOTES"]) # Calculate the new number of votes await message.channel.send( f"""πͺ {message.author.mention} removes their vote to end the MiniMiniTWOW. There are now **{votes}{nec_seg}** votes.""") return mmt.info["GAME"]["END_VOTES"].append( message.author.id) # Add user's ID to list of end votes votes = len( mmt.info["GAME"]["END_VOTES"]) # Calculate new number of votes await message.channel.send( f"""πͺ **{message.author.mention} voted to end the MiniMiniTWOW!** There are now **{votes}{nec_seg}** votes.""") if votes >= necessary: # If the amount of votes reaches the required... await message.channel.send( """**The current MiniMiniTWOW has been ended by public vote! The queue moves on.**""".replace("\n", "").replace("\t", "")) mmt.force_skip( ) # Function to skip a host, also used in Events/mmt.py return if args[1].lower() == "transfer": if not mmt.RUNNING: await message.channel.send( "There's no MiniMiniTWOW running right now!") return if mmt.info["GAME"]["PERIOD"] == 0: await message.channel.send( "Host transfers can only occur with MiniMiniTWOWs that have been created." ) return if mmt.info["GAME"]["HOST"] != message.author.id: await message.channel.send( "Only the host can transfer the MiniMiniTWOW to someone else!") return if level == 2: await message.channel.send("Choose a user to transfer it to!") return # You have to ping the person you wanna transfer it to mention = args[2] try: user_id = int(mention[2:-1]) except: await message.channel.send( "Invalid user! Ping the user you want to transfer the MiniMiniTWOW to!" ) return # Try to find the person in TWOW Central person = TWOW_CENTRAL.get_member(user_id) if person is None: # If they're not in TWOW Central, they can't become the new host await message.channel.send( "Invalid user! Ping the user you want to transfer the MiniMiniTWOW to!" ) return if 'confirm' not in [ x.lower() for x in args ]: # Confirmation can be bypassed by including `confirm` await message.channel.send( f"""Are you sure you want to transfer the MiniMiniTWOW to **{person.name}**? Send `confirm` to transfer.""".replace("\n", "").replace("\t", "")) # Check for the next message by the same person in the same channel msg = await BRAIN.wait_for( 'message', check=lambda m: (m.author == message.author and m.channel == message.channel)) if msg.content.lower( ) != "confirm": # If it's not `confirm`, cancel the command await message.channel.send("Transfer command cancelled.") return msg_send = f"Successfully transfered host position to **{person.name}**!" # Make the new person the host mmt.info["GAME"]["HOST"] = user_id # Reset the timers for host actions if necessary if mmt.info["GAME"][ "PERIOD"] == 1 and mmt.info["GAME"]["PERIOD_START"] != 0: mmt.info["GAME"]["PERIOD_START"] = time.time() msg_send += f""" The timer to start the MiniMiniTWOW is reset. {mention} has {mmt.param["S_DEADLINE"]} seconds to start it with `tc/mmt start`.""".replace("\n", "").replace("\t", "") if mmt.info["GAME"]["PERIOD"] == 2: mmt.info["GAME"]["PERIOD_START"] = time.time() msg_send += f""" The timer to set the prompt is reset. {mention} has {mmt.param["P_DEADLINE"]} seconds to decide on the prompt with `tc/mmt prompt`.""".replace( "\n", "").replace("\t", "") await message.channel.send(msg_send) return if args[1].lower() == "queue": if level == 2: # If it's just `tc/mmt queue` if not mmt.RUNNING: # The event is counted as off if there's nobody in queue. To check the queue, it needs # to be on, so being the first to join the queue will start up the event mmt.start(TWOW_CENTRAL) if message.author.id in mmt.info[ "HOST_QUEUE"]: # If you're already in queue, leaves the queue mmt.info["HOST_QUEUE"] = [ x for x in mmt.info["HOST_QUEUE"] if x != message.author.id ] await mmt.MMT_C.send( f"π© <@{message.author.id}> has been removed from queue.") return mmt.info["HOST_QUEUE"].append( message.author.id ) # If you're not in queue, you're added to queue await message.channel.send( f"π© <@{message.author.id}> has been added to queue at position **{len(mmt.info['HOST_QUEUE'])}**." ) return if args[2].lower() == "list": if len( mmt.info["HOST_QUEUE"] ) == 0: # If the event is not running, the host queue's length is also 0 await message.channel.send("The queue is empty!") return # Array I use to split the list into multiple messages if necessary init = ["**This is the current MiniMiniTWOW hosting queue:**\n\n"] for person in mmt.info["HOST_QUEUE"]: # `person` is an ID member = TWOW_CENTRAL.get_member( person) # Try to find the user if member is None: # If you can't find the user, just use their ID and format it member = f"`{person}`" else: # If you found the user, use their username member = member.name # Define the line that's about to be added line = f"π© [{mmt.info['HOST_QUEUE'].index(person) + 1}] - **{member}**\n" # If adding this line steps over the character limit, add another string to the array # 1950 instead of 2000 because having over 1950 can cause issues for like no reason if len(init[-1] + line) > 1950: line.append("") init[-1] += line # Add the line to this new string for z in init: # Send each of those strings as a separate message await message.channel.send(z) return if args[1].lower() == "create": if not mmt.RUNNING: # If the event isn't running await message.channel.send( f"There's no host to create a MiniMiniTWOW! Join the queue with `{PREFIX}mmt queue` to host!" ) return if mmt.info["GAME"]["PERIOD"] != 0: # If it's not time to create one await message.channel.send( "There's already a MiniMiniTWOW running!") return if message.author.id != mmt.info["GAME"]["HOST"]: # If you're not host await message.channel.send( "You can only create a MiniMiniTWOW if you're up on the queue!" ) return # Switch the period to 1 (signups) mmt.info["GAME"]["PERIOD"] = 1 mmt.info["GAME"]["PERIOD_START"] = 0 await message.channel.send( f"π© <@{message.author.id}> has created a MiniMiniTWOW! Use `{PREFIX}mmt join` to join it!" ) return if args[1].lower() == "spectate": if not mmt.RUNNING: # If the event isn't running await message.channel.send( "There's no MiniMiniTWOW running right now!") return if mmt.info["GAME"][ "PERIOD"] == 0: # If `tc/mmt create` hasn't been run await message.channel.send( "You can only spectate a MiniMiniTWOW that has been created!") return if message.author.id in mmt.info[ "SPECTATORS"]: # If you're a spectator... # ...You're removed from the list of spectators mmt.info["SPECTATORS"] = [ x for x in mmt.info["SPECTATORS"] if x != message.author.id ] await mmt.MMT_C.send( f"ποΈ <@{message.author.id}> is no longer spectating.") return mmt.info["SPECTATORS"].append( message.author.id) # Adds user to the list of spectators await mmt.MMT_C.send( f"""ποΈ <@{message.author.id}> is now spectating, and will receive voting screens for future rounds.""".replace("\n", "").replace("\t", "")) return if args[1].lower() == "join": if not mmt.RUNNING: # If the event is not running await message.channel.send( "There's no MiniMiniTWOW running right now!") return if mmt.info["GAME"]["PERIOD"] != 1: # If it's not signup period await message.channel.send( "You can only join the MiniMiniTWOW if it's in signups!") return if message.author.id in mmt.info[ "PLAYERS"]: # If you're already a player # Remove you both from the player list and the spectator list mmt.info["PLAYERS"] = [ x for x in mmt.info["PLAYERS"] if x != message.author.id ] mmt.info["SPECTATORS"] = [ x for x in mmt.info["SPECTATORS"] if x != message.author.id ] await mmt.MMT_C.send( f"π <@{message.author.id}> left the MiniMiniTWOW. Our player count is {len(mmt.info['PLAYERS'])}." ) if len( mmt.info['PLAYERS'] ) == 1: # The timer is cancelled if there's no longer 2 players await mmt.MMT_C.send( "π We no longer have two players. The three minute start timer is now reset." ) return mmt.info["PLAYERS"].append(message.author.id) # Adds user as a player if message.author.id not in mmt.info["SPECTATORS"]: mmt.info["SPECTATORS"].append( message.author.id ) # And a spectator, if they're not already one await mmt.MMT_C.send( f"π **<@{message.author.id}> joined the MiniMiniTWOW!** Our player count is now {len(mmt.info['PLAYERS'])}!" ) if len(mmt.info['PLAYERS'] ) == 2: # If there are two players, the signup timer begins await mmt.MMT_C.send( f"""π We have two players! <@{mmt.info["GAME"]["HOST"]}> has three minutes to start the MiniMiniTWOW with `{PREFIX}mmt start`.""".replace( "\n", "").replace("\t", "")) mmt.info["GAME"]["PERIOD_START"] = time.time() return if args[1].lower() == "start": if not mmt.RUNNING: # If the event isn't running await message.channel.send( "There's no MiniMiniTWOW running right now!") return if mmt.info["GAME"]["PERIOD"] != 1: # If it's not signup period await message.channel.send( "You can only start a MiniMiniTWOW if it's in signups!") return if message.author.id != mmt.info["GAME"]["HOST"]: # If user isn't host await message.channel.send( "Only the host can start a MiniMiniTWOW!") return if len(mmt.info["PLAYERS"]) < 2: # If there aren't two contestants await message.channel.send( "You need at least two contestants to start a MiniMiniTWOW!") return # Set it to round 1 with period 2 (prompt deciding) mmt.info["GAME"]["ROUND"] = 1 mmt.info["GAME"]["PERIOD"] = 2 mmt.info["GAME"]["PERIOD_START"] = time.time() await mmt.MMT_C.send( f"""π <@{message.author.id}> has started the MiniMiniTWOW with {len(mmt.info["PLAYERS"])} contestants. Nobody can sign up anymore.""". replace("\n", "").replace("\t", "")) return if args[1].lower() == "prompt": if not mmt.RUNNING: # If the event isn't running await message.channel.send( "There's no MiniMiniTWOW running right now!") return if mmt.info["GAME"][ "PERIOD"] != 2: # If it's not prompt decision period await message.channel.send( "You can only set a prompt inbetween rounds!") return if message.author.id != mmt.info["GAME"]["HOST"]: # If user isn't host await message.channel.send("Only the host can set a prompt!") return if level == 2: await message.channel.send("You need to include a prompt!") return # Remove formatting and characters that might cause errors prompt = " ".join(args[2:]).replace("`", "").replace("\t", "").replace("\n", "") if len(prompt) > 200: await message.channel.send( "That prompt is too long! It must be 200 characters at most.") return # Switch to responding period, set the rpompt, prepare the responses array mmt.info["GAME"]["PERIOD"] = 3 mmt.info["GAME"]["PERIOD_START"] = time.time() mmt.info["GAME"]["PROMPT"] = prompt mmt.info["RESPONSES"] = [""] * len(mmt.info["PLAYERS"]) await mmt.MMT_C.send( f"""π **Round {mmt.info["GAME"]["ROUND"]} Responding** has started! The prompt is: ```{prompt}``` Our contestants have {mmt.param["R_DEADLINE"]} seconds to respond to it.""". replace("\n", "").replace("\t", "")) for player in mmt.info["PLAYERS"]: try: # DM everyone the prompt await TWOW_CENTRAL.get_member(player).send(f""" π **Round {mmt.info["GAME"]["ROUND"]} Responding** has started! The prompt is: ```{prompt}``` You must respond in {mmt.param["R_DEADLINE"]} seconds using `{PREFIX}mmt respond`! """.replace("\n", "").replace("\t", "")) except Exception: # If something goes wrong, skip the person pass return if args[1].lower() == "respond": if not isinstance(message.channel, discord.DMChannel): # If not in DMs await mmt.MMT_C.send("This command can only be used in DMs!") return if not mmt.RUNNING: # If event isn't running await message.channel.send( "There's no MiniMiniTWOW running right now!") return if mmt.info["GAME"]["PERIOD"] != 3: # If it's not submission period await message.channel.send( "You can only respond during a submission period!") return if message.author.id not in mmt.info[ "PLAYERS"]: # If you're not a player await message.channel.send("Only alive contestants can respond!") return if level == 2: await message.channel.send("You need to include a response!") return # Run the formatting_fix function, remove characters that might cause issues and strip unnecessary whitespace response = formatting_fix(" ".join(args[2:]).replace("`", "").replace( "\t", "").replace("\n", "")).strip() if len(response) > 120: # Too long await message.channel.send( "Your response is too long! It must be 120 characters at most." ) return if len(response) == 0: # Literally empty await message.channel.send( "Your response evaluates to an empty string.") return ind = mmt.info["PLAYERS"].index( message.author.id) # Index of the user in the players array new = not mmt.info["RESPONSES"][ ind] == "" # Detects if this is a new response or an edit while response in mmt.info[ "RESPONSES"]: # If this response has already been submitted... response += "\u200b" # ...distinguish between them by adding a zero-width space to the end of one # Keep doing so until it's unique (in the case there's more than two identical entries) mmt.info["RESPONSES"][ ind] = response # Set the response to the one submitted await message.channel.send( f"""Your {'new ' if new else ''}response to the prompt has been recorded as: ```{response}```> **Word count:** {word_count(response)}""".replace( "\n", "").replace("\t", "")) return if args[1].lower() == "vote": if not isinstance(message.channel, discord.DMChannel): # If not in DMs await mmt.MMT_C.send("This command can only be used in DMs!") return if not mmt.RUNNING: # If event isn't running await message.channel.send( "There's no MiniMiniTWOW running right now!") return if message.author.id not in mmt.info["VOTES"][ "ID"]: # If you didn't receive a voting screen await message.channel.send( "You can only vote if you received a voting screen!") return if mmt.info["GAME"]["PERIOD"] != 4: # If it's not voting period await message.channel.send( "You can only vote during a voting period!") return if level == 2: await message.channel.send("You need to include a vote!") return vote = args[2].upper() # Always convert vote to uppercase ind = mmt.info["VOTES"]["ID"].index( message.author.id) # Index of user in the votes arrays screen = mmt.info["VOTES"]["RESP"][ ind] # Check the screen the user received # The screen has `n` responses, so the vote should be composed of the first `n` letters of the alphabet # Otherwise, it's invalid if sorted(list(vote)) != sorted(list(ALPHABET[:len(screen)])): await message.channel.send( """Your vote is invalid. Make sure you're not missing or repeating any letters, or including any invalid characters.""".replace("\n", "").replace("\n", "")) return # Instantly convert the vote to the score it'll give to each response parsed_vote = [] for z in range(len(vote)): score = (len(vote) - 1 - vote.find(ALPHABET[z])) / (len(vote) - 1) parsed_vote.append(str(score)) new = not mmt.info["VOTES"]["VOTE"][ ind] == "" # Detect if the vote is new or an edit mmt.info["VOTES"]["VOTE"][ind] = " ".join( parsed_vote) # Join the list of values and record it # Display the vote back as the letters await message.channel.send( f"""Your {'new ' if new else ''}vote has been recorded as: ```{vote}```""".replace("\n", "").replace("\t", "")) return