Beispiel #1
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
Beispiel #3
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
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", ""))
Beispiel #6
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

        runner = message.author

    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]

        runner = message.author

    try:
        program_output = run_bpp_program(program, program_args, author, runner)
        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