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, 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
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, 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 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): 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): 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", ""))