Esempio n. 1
0
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
Esempio n. 2
0
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
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 6
0
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
Esempio n. 7
0
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
Esempio n. 8
0
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", ""))