import discord
from discord_components import *
import sqlite3
import requests
import json
from datetime import datetime, time
from pytz import timezone as tz
import math

submissionsChannel = 898695908454182912
automationRole = 906749972999966741
actionLogChannel = 910022427046731817

async def sSubmit(ctx, gid, discordClient, roClient):
    isHighRank = False
    roles = ctx.message.author.roles
    for i in range(len(roles)):
        if roles[i].id == 909593464155553862 or ctx.message.author.guild_permissions.administrator:
            isHighRank = True
    if not isHighRank:
        await ctx.send("You cannot use this command.")
        return

    db = sqlite3.connect("main.sqlite")
    cursor = db.cursor()

    groupFound = False
    for row in cursor.execute('SELECT * FROM groups ORDER BY points'):
        if row[0] == gid:
            groupFound = True
            break
    if not groupFound:
        await ctx.send("This group is not in the database.")
        return

    await ctx.send("Check your dms! (If you did not get one, please enable \"Allow direct messages from server members\" in your Privacy and Safety settings)")
    dm = await ctx.message.author.create_dm()

    place = None
    type = None
    defaultNumbers = "N/A"
    name = None

    submitPrompt = discord.Embed(
        title="RR:H - SUBMIT PROMPT",
        description="Please select one of the 3 buttons below which best describes what you're trying to do.",
        color=16711680
    )
    submitPrompt.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
    components = [
        Button(style=ButtonStyle.gray, label="New Submission", custom_id="newSub"),
        Button(style=ButtonStyle.gray, label="Edit Submission", custom_id="editSub"),
        Button(style=ButtonStyle.gray, label="Delete Submission", custom_id="deleteSub")
    ]
    msg = await dm.send(embed=submitPrompt, components=[components])

    def check(m):
        return m.channel == dm and m.author == ctx.message.author

    def buttonCheck(b):
        return b.channel == dm

    standardTimezone = "UTC"
    displayTimezones = ["EST", "GMT"]

    def addSecondsToTime(values, seconds):
        o_mins = seconds / 60
        o_hours = math.floor(o_mins / 60)
        o_mins -= o_hours * 60
        hours, mins = values[0], values[1]
        mins += o_mins
        if mins >= 60:
            mins -= 60
            o_hours += 1
        hours += o_hours
        if hours >= 24:
            hours -= 24
        return [int(hours), int(mins)]

    def toDisplayTimes(values):  # takes [hours,mins]
        standard = tz(standardTimezone)
        dt = datetime.now()
        displayText = None
        for timezone in displayTimezones:
            offset = (tz(timezone).utcoffset(dt) - standard.utcoffset(dt)).seconds
            new = addSecondsToTime(values, offset)
            localisedTime = time(new[0], new[1]).strftime("%H:%M")
            if not displayText:
                displayText = f'{localisedTime} {timezone}'
            else:
                displayText = displayText + f", {localisedTime} {timezone}"
        return displayText

    def processTime(str):  # returns in [hours,mins] format
        raw = str.split(":")
        values = [23, 59]
        for i in range(2):
            new = raw[i]
            if not new.isdigit() or len(new) < 2:
                return None
            new = int(new)
            if new >= 0 and new <= values[i]:
                values[i] = new
            else:
                return None
        return values

    async def onMisinput(attempt):
        if attempt < 3:
            attemptEmbed = discord.Embed(
                title=":x: ERROR",
                description=f"{attempt}/3 | Invalid format was received!\nTry again following the valid format",
                color=16711680
            )
            await dm.send(embed=attemptEmbed)
            newMsg = await discordClient.wait_for("message", check=check)

            return newMsg
        else:
            attemptEmbed = discord.Embed(
                title=":x: ERROR",
                description=f"{attempt}/3 | You are out of tries!\nRun the command ``{ctx.message.content}`` again to continue.",
                color=16711680
            )
            await dm.send(embed=attemptEmbed)

            return False

    async def newSub():
        global prefloodTimes, notes
        placePrompt = discord.Embed(
            title="RR:H - SUBMIT PROMPT",
            description="Please enter the ID of the place you wish to add to your profile.",
            color=16711680
        )
        placePrompt.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
        await dm.send(embed=placePrompt)

        msg = await discordClient.wait_for('message', check=check)
        name = None
        place = None
        for i in range(1, 4):
            place = msg.content
            placeLink = requests.get(f"https://api.roblox.com/Marketplace/ProductInfo?assetId={place}")
            placeParse = json.loads(placeLink.text)
            name = placeParse["Name"]
            if name:
                break
            msg = await onMisinput(i)
            if not msg:
                return

        typeEmbed = discord.Embed(
            title="RR:H - SUBMIT PROMPT",
            description="Please select the type of thing you wish to submit.",
            color=16711680
        )
        typeEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
        components = [
            Button(style=ButtonStyle.gray, label="Fort", custom_id="fort"),
            Button(style=ButtonStyle.gray, label="Fairzone", custom_id="fairzone")
        ]
        msg = await dm.send(embed=typeEmbed, components=[components])

        prefloodTimes = "N/A"

        res0 = await discordClient.wait_for("button_click", check=buttonCheck)
        if res0.channel == dm:
            await msg.edit(embed=typeEmbed, components=[])

            type = res0.component.custom_id

            defaultNumbersEmbed = discord.Embed(
                title="RR:H - SUBMIT PROMPT",
                description="Please enter the following information about your base **IN THE GIVEN FORMAT.**",
                color=16711680
            )
            defaultNumbersEmbed.add_field(name="FORMAT:", value="```Default Numbers - Static/Non-Static```", inline=False)
            defaultNumbersEmbed.add_field(name="EXAMPLE:", value="10v10 - Static", inline=False)
            defaultNumbersEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
            await dm.send(embed=defaultNumbersEmbed)

            msg = await discordClient.wait_for('message', check=check)
            for i in range(1, 4):
                if (msg.content[0].isnumeric() or msg.content[1].isnumeric()) and (
                        msg.content[2].isnumeric() or msg.content[4].isnumeric()):
                    if (msg.content[6:] == "Static" or msg.content[6:] == "Non-Static") or (
                            msg.content[8:] == "Static" or msg.content[8:] == "Non-Static"):
                        break
                msg = await onMisinput(i)
                if not msg:
                    return

            defaultNumbers = msg.content

            prefloodEmbed = discord.Embed(
                title="RR:H - SUBMIT PROMPT",
                description=f"Is {name} prefloodable?",
                color=16711680
            )
            prefloodEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
            components = [
                Button(style=ButtonStyle.green, label="Yes", custom_id="yes"),
                Button(style=ButtonStyle.red, label="No", custom_id="no")
            ]
            msg = await dm.send(embed=prefloodEmbed, components=[components])

            res1 = await discordClient.wait_for("button_click", check=buttonCheck)
            if res1.channel == dm and res1.component.custom_id == "yes":
                await msg.edit(embed=prefloodEmbed, components=[])

                earlyEmbed = discord.Embed(
                    title="RR:H - SUBMIT PROMPT",
                    description=f"Please time the **earliest** time {name} is open to be preflooded in **UTC 24-hour time [HH:mm]**.\n\ne.g: ``13:00``, ``08:00``.\n\n*Use the [time zone converter](https://dateful.com/time-zone-converter) site if uncertain of EST.*",
                    color=16711680
                )
                earlyEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
                await dm.send(embed=earlyEmbed)

                msg1 = await discordClient.wait_for('message', check=check)
                firstTime = None
                for i in range(1, 4):
                    test = processTime(msg1.content)
                    if test:
                        firstTime = toDisplayTimes(test).rsplit(" ")
                        break
                    msg1 = await onMisinput(i)
                    if not msg1:
                        return

                lateEmbed = discord.Embed(
                    title="RR:H - SUBMIT PROMPT",
                    description=f"Please time the **latest** time {name} is open to be preflooded in **UTC 24-hour time [HH:mm]**.\n\ne.g: ``13:00``, ``08:00``.\n\n*Use the [time zone converter](https://dateful.com/time-zone-converter) site if uncertain of EST.*",
                    color=16711680
                )
                lateEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
                await dm.send(embed=lateEmbed)

                msg2 = await discordClient.wait_for('message', check=check)
                lastTime = None
                for i in range(1, 4):
                    test = processTime(msg2.content)
                    if test:
                        lastTime = toDisplayTimes(test).rsplit(" ")
                        break
                    msg2 = await onMisinput(i)
                    if not msg2:
                        return

                prefloodTimes = f"{firstTime[0]}-{lastTime[0]} (EST), {firstTime[2]}-{lastTime[2]} (GMT)"

            notesEmbed = discord.Embed(
                title="RR-H - SUBMIT PROMPT",
                description="Are there any notes you wish to include for people to know about this place?\n\nIf not, please type ``no``.",
                color=16711680
            )
            notesEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
            await dm.send(embed=notesEmbed)

            msg = await discordClient.wait_for('message', check=check)
            notes = "N/A"
            if msg.content != "no":
                notes = msg.content

            infoEmbed = discord.Embed(
                title="RR:H - SUBMIT PROMPT",
                description=f"**Does this information look correct?**\n\n[{name}](https://www.roblox.com/games/{place})",
                color=16711680
            )
            infoEmbed.add_field(name="Default Numbers: ", value=defaultNumbers, inline=False)
            infoEmbed.add_field(name="Preflood Times: ", value=prefloodTimes, inline=False)
            infoEmbed.add_field(name="Notes: ", value=notes, inline=False)
            infoEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
            components = [
                Button(style=ButtonStyle.blue, label="Yes", custom_id="yes0"),
                Button(style=ButtonStyle.red, label="No", custom_id="no0")
            ]

            msg = await dm.send(embed=infoEmbed, components=[components])

            res2 = await discordClient.wait_for("button_click", check=buttonCheck)
            if res2.channel == dm and res2.component.custom_id == "yes0":
                await msg.edit(embed=infoEmbed, components=[])

                group = await roClient.get_group(group_id=gid)
                confirmPrompt = discord.Embed(
                    title="RR:H - SUBMIT PROMPT",
                    description=f"Thank you for using the RR:H submission service!\nYour represented group [{group.name}](https://www.roblox.com/groups/{gid}) will be rewarded **5 points** when the application is accepted!",
                    color=65280  # Green
                )
                confirmPrompt.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
                await dm.send(embed=confirmPrompt)

                role = ctx.message.guild.get_role(automationRole)
                modChannel = discordClient.get_channel(id=submissionsChannel)

                submitApplication = discord.Embed(
                    title="RR:H - SUBMIT APPLICATION",
                    color=16711680
                )
                submitApplication.add_field(name="Name:", value=f"[{name}](https://www.roblox.com/games/{place})", inline=False)
                submitApplication.add_field(name="Type:", value=type, inline=False)
                submitApplication.add_field(name="Default Numbers:", value=defaultNumbers, inline=False)
                submitApplication.add_field(name="Preflood Times:", value=prefloodTimes, inline=False)
                submitApplication.add_field(name="Notes:", value=notes, inline=False)
                submitApplication.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
                submitApplication.set_author(name=ctx.message.author, icon_url=ctx.message.author.avatar_url)
                submitApplication.set_footer(text=f"User ID: {ctx.message.author.id}")
                components = [
                    Button(style=ButtonStyle.blue, label="Accept", custom_id="accept"),
                    Button(style=ButtonStyle.red, label="Decline", custom_id="decline")
                ]
                msg = await modChannel.send(f"{role.mention} | A new clan is requesting to upload a submission to RR:H.", embed=submitApplication, components=[components])

                def modChannelCheck(b):
                    return b.channel == modChannel

                res3 = await discordClient.wait_for("button_click", check=modChannelCheck)
                if res3.channel == modChannel and res3.component.custom_id == "accept":

                    await dm.send("Your application was accepted!")
                    newComponents = [
                        Button(style=ButtonStyle.green, label="Accept", disabled=True),
                        Button(style=ButtonStyle.gray, label="Decline", disabled=True)
                    ]
                    await msg.edit(f"{role.mention} | {res3.user.mention} has accepted the following submission.", embed=submitApplication, components=[newComponents])
                    await res3.respond(content=":thumbsup:")
                    bigRow = None
                    for row in cursor.execute('SELECT * FROM groups ORDER BY points'):
                        if row[0] == gid:
                            bigRow = row
                    results = bigRow[7]
                    addition = str(int(results) + 5)
                    cursor.execute("UPDATE groups SET points =? WHERE groupid =?", (addition, gid))
                    cursor.execute("INSERT INTO places VALUES (?, ?, ?, ?, ?, ?)",
                                   (str(place), str(gid), str(type), str(defaultNumbers), str(prefloodTimes), str(notes)))
                    db.commit()

                    logEmbed = discord.Embed(
                        title=":white_check_mark: __Submission Action Log:__",
                        description=f"**{group.name}** has added a new {type} to Raid Request: Hub!\n\n**Name:** ``{name}``\n**Default Numbers:** ``{defaultNumbers}``\n**Preflood Times:** ``{prefloodTimes}`` ```{notes}```",
                        color=65280
                    )
                    logEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
                    logChannel = discordClient.get_channel(actionLogChannel)
                    await logChannel.send(embed=logEmbed)
                elif res3.channel == modChannel and res3.component.custom_id == "decline":
                    newComponents = [
                        Button(style=ButtonStyle.gray, label="Accept", disabled=True),
                        Button(style=ButtonStyle.green, label="Decline", disabled=True)
                    ]
                    await dm.send("Your application has been declined.")
                    await res3.respond(content=":thumbsup:")
                    await msg.edit(
                        f"{role.mention} | {res3.user.mention} has declined the following submission.", embed=submitApplication, components=[newComponents])

            elif res2.channel == dm and res2.component.custom_id == "no0":
                await msg.edit(embed=infoEmbed, components=[])

                await dm.send(content=":x: Submission not sent.")
                return

    async def editSub():
        enterEmbed = discord.Embed(
            title="RR:H - SUBMIT PROMPT",
            description="Please enter the ID of the place you wish to edit.",
            color=16776960
        )
        enterEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
        await dm.send(embed=enterEmbed)

        msg = await discordClient.wait_for('message', check=check)
        for i in range(1, 4):
            if str(msg.content).isnumeric():
                break
            msg = await onMisinput(i)
            if not msg:
                return

        place = msg.content

        selectEmbed = discord.Embed(
            title="RR:H - SUBMIT PROMPT",
            description="Please select what you want to edit.",
            color=16776960
        )
        selectEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
        components = [
            Button(style=ButtonStyle.gray, label="Base Information", custom_id="base"),
            Button(style=ButtonStyle.gray, label="Prefloodable Value", custom_id="preflood"),
            Button(style=ButtonStyle.gray, label="Notes", custom_id="notes")
        ]
        msg = await dm.send(embed=selectEmbed, components=[components])

        res0 = await discordClient.wait_for("button_click", check=buttonCheck)
        if res0.channel == dm and res0.component.custom_id == "base":
            await msg.edit(embed=selectEmbed, components=[])

            defaultNumbersEmbed = discord.Embed(
                title="RR:H - SUBMIT PROMPT",
                description="Please enter the following information about your base **IN THE GIVEN FORMAT.**",
                color=16711680
            )
            defaultNumbersEmbed.add_field(name="FORMAT:", value="```Default Numbers - Static/Non-Static```",
                                          inline=False)
            defaultNumbersEmbed.add_field(name="EXAMPLE:", value="10v10 - Static", inline=False)
            defaultNumbersEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
            await dm.send(embed=defaultNumbersEmbed)

            msg = await discordClient.wait_for('message', check=check)
            for i in range(1, 4):
                if (msg.content[0].isnumeric() or msg.content[1].isnumeric()) and (
                        msg.content[2].isnumeric() or msg.content[4].isnumeric()):
                    if (msg.content[6:] == "Static" or msg.content[6:] == "Non-Static") or (
                            msg.content[8:] == "Static" or msg.content[8:] == "Non-Static"):
                        break
                msg = await onMisinput(i)
                if not msg:
                    return

            cursor.execute(f"UPDATE places SET defaultNumbers = ? WHERE placeid = {place}", (msg.content,))
            db.commit()

            await dm.send(":white_check_mark: **Base Information Updated.**")
        elif res0.channel == dm and res0.component.custom_id == "preflood":
            await msg.edit(embed=selectEmbed, components=[])

            prefloodTimes = "N/A"

            prefloodEmbed = discord.Embed(
                title="RR:H - SUBMIT PROMPT",
                description=f"Is your place prefloodable?",
                color=16711680
            )
            prefloodEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
            components = [
                Button(style=ButtonStyle.green, label="Yes", custom_id="yes"),
                Button(style=ButtonStyle.red, label="No", custom_id="no")
            ]
            msg = await dm.send(embed=prefloodEmbed, components=[components])

            res1 = await discordClient.wait_for("button_click", check=buttonCheck)
            if res1.channel == dm and res1.component.custom_id == "yes":
                await msg.edit(embed=prefloodEmbed, components=[])

                earlyEmbed = discord.Embed(
                    title="RR:H - SUBMIT PROMPT",
                    description=f"Please time the **earliest** time {name} is open to be preflooded in **UTC 24-hour time [HH:mm]**.\n\ne.g: ``13:00``, ``08:00``.\n\n*Use the [time zone converter](https://dateful.com/time-zone-converter) site if uncertain of EST.*",
                    color=16711680
                )
                earlyEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
                await dm.send(embed=earlyEmbed)

                msg1 = await discordClient.wait_for('message', check=check)
                firstTime = None
                for i in range(1, 4):
                    test = processTime(msg1.content)
                    if test:
                        firstTime = toDisplayTimes(test).rsplit(" ")
                        break
                    msg1 = await onMisinput(i)
                    if not msg1:
                        return

                lateEmbed = discord.Embed(
                    title="RR:H - SUBMIT PROMPT",
                    description=f"Please time the **latest** time {name} is open to be preflooded in **UTC 24-hour time [HH:mm]**.\n\ne.g: ``13:00``, ``08:00``.\n\n*Use the [time zone converter](https://dateful.com/time-zone-converter) site if uncertain of EST.*",
                    color=16711680
                )
                lateEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
                await dm.send(embed=lateEmbed)

                msg2 = await discordClient.wait_for('message', check=check)
                lastTime = None
                for i in range(1, 4):
                    test = processTime(msg2.content)
                    if test:
                        lastTime = toDisplayTimes(test).rsplit(" ")
                        break
                    msg2 = await onMisinput(i)
                    if not msg2:
                        return

                prefloodTimes = f"{firstTime[0]}-{lastTime[0]} (EST), {firstTime[2]}-{lastTime[2]} (GMT)"

            cursor.execute(f"UPDATE places SET prefloodTimes = ? WHERE placeid = {place}", (prefloodTimes,))
            db.commit()

            await dm.send(":white_check_mark: **Preflood Times Updated.**")
        elif res0.channel == dm and res0.component.custom_id == "notes":
            await msg.edit(embed=selectEmbed, components=[])

            notesEmbed = discord.Embed(
                title="RR-H - SUBMIT PROMPT",
                description="Are there any notes you wish to include for people to know about this place?\n\nIf not, please type ``no``.",
                color=16711680
            )
            notesEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
            await dm.send(embed=notesEmbed)

            msg = await discordClient.wait_for('message', check=check)
            notes = "N/A"
            if msg.content != "no":
                notes = msg.content

            cursor.execute(f"UPDATE places SET notes = ? WHERE placeid = {place}", (notes,))
            db.commit()

            await dm.send(":white_check_mark: **Notes Updated.**")

    async def deleteSub():
        enterEmbed = discord.Embed(
            title="RR:H - SUBMIT PROMPT",
            description="Please enter the ID of the place you wish to delete.",
            color=16776960
        )
        enterEmbed.set_thumbnail(url="https://i.imgur.com/O0qaTbb.jpg")
        await dm.send(embed=enterEmbed)

        msg = await discordClient.wait_for('message', check=check)
        for i in range(1, 4):
            if str(msg.content).isnumeric():
                break
            msg = await onMisinput(i)
            if not msg:
                return

        place = msg.content

        cursor.execute(f"DELETE FROM places WHERE placeid = {place}")
        db.commit()

        await dm.send(":white_check_mark: **Submission Deleted.**")

    res = await discordClient.wait_for("button_click", check=buttonCheck)
    if res.channel == dm and res.component.custom_id == "newSub":
        await msg.edit(embed=submitPrompt, components=[])
        await newSub()
    elif res.channel == dm and res.component.custom_id == "editSub":
        await msg.edit(embed=submitPrompt, components=[])
        await editSub()
    elif res.channel == dm and res.component.custom_id == "deleteSub":
        await msg.edit(embed=submitPrompt, components=[])
        await deleteSub()


from django.conf import settings
import requests

FAST_2_SMS_BASE_URL = "https://www.fast2sms.com/dev/bulkV2"

def send_sms_for_order_status_change(order):
    message = settings.SMS_MESSAGES.get(({
        settings.ORDER_STATUS_PLACED: None,
        settings.ORDER_STATUS_CONFIRMED: 'ORDER_CONFIRMED',
        settings.ORDER_STATUS_PACKED: None,
        settings.ORDER_STATUS_SHIPPED: 'ORDER_SHIPPED',
        settings.ORDER_STATUS_OUT_FOR_DELIVERY: "OUT FOR DELIVERY",
        settings.ORDER_STATUS_DELIVERED: 'ORDER_DELIVERED',
        settings.ORDER_STATUS_RETURN_REQUESTED: None,
        settings.ORDER_STATUS_REFUND_APPROVED: 'RETURN_INITIATED',
        settings.ORDER_STATUS_REPLACEMENT_APPROVED: 'REPLACEMENT_INITIATED',
        settings.ORDER_STATUS_RETURNED: 'RETURNED',
        settings.ORDER_STATUS_REPLACED: None,
        settings.ORDER_STATUS_CANCELED: 'ORDER_CANCELED',
        settings.ORDER_STATUS_PAYMENT_DECLINED: 'PAYMENT_DECLINED',
    }).get(order.status))
    try:
        mob_no = ",".join([num[-10:] for num in order.user.mobile if num])
        mob_no = mob_no.replace(",", "")
        if message and mob_no:
            print("calling send_p_sms", message, mob_no)
            return send_p_sms(mob_no, message=message.format(order=order))
    except Exception as e:
        try:
            order.notes.create(user=None, message='Could not send SMS', note_type='Info')
        except:
            pass

class Fast2SMS:
    def __init__(self):
        self.url = FAST_2_SMS_BASE_URL

    def send_p_sms(self, phone_no, message):
        url = self.url

        querystring = {
            "authorization": settings.FAST_2_SMS_API_KEY,
            # "sender_id": getattr(settings, 'FAST_2_SMS_SENDER_ID', "SMSINI"),
            "sender_id": "TXTIND",
            # "language": "english",
            "route": "v3",
            "numbers": phone_no,
            "message": message,
        }
        headers = {'cache-control': "no-cache"}
        response = requests.request("GET", url, headers=headers, params=querystring)
        print(response.text, "$$$ sent $$$$")
        return True

    def send_qt_sms(self, phone_no, message):
        url = self.url

        querystring = {
            "authorization": settings.FAST_2_SMS_API_KEY,
            "sender_id": "TXTIND",
            # "sender_id": settings.FAST_2_SMS_SENDER_ID,
            "language": "english",
            "route": "dlt",
            "numbers": phone_no,
            "message": settings.FAST_2_SMS_TEMPLATE_ID,
            "variables_values": "%s" % message
        }
        headers = {'cache-control': "no-cache"}
        response = requests.request("GET", url, headers=headers, params=querystring)
        print(response.text)
        return True

def send_p_sms(phone_no, message):
    print("calling fast 2 class")
    return Fast2SMS().send_p_sms(phone_no, message)

def send_qt_sms(phone_no, message):
    return Fast2SMS().send_qt_sms(phone_no, message)

######################################################################

# def _send_p_sms(phone_no, message):
#     url = "https://www.fast2sms.com/dev/bulk"
#
#     querystring = {
#         "authorization": settings.FAST_2_SMS_API_KEY,
#         "sender_id": getattr(settings, 'FAST_2_SMS_SENDER_ID', "SMSINI"),
#         "language": "english",
#         "route": "p",
#         "numbers": phone_no,
#         "message": message,
#     }
#     headers = {'cache-control': "no-cache"}
#     response = requests.request("GET", url, headers=headers, params=querystring)
#     print(response.text)
#     return True
#
#
# def _send_qt_sms(phone_no, message):
#     url = "https://www.fast2sms.com/dev/bulk"
#
#     querystring = {
#         "authorization": settings.FAST_2_SMS_API_KEY,
#         "sender_id": "BathxB",
#         "language": "english",
#         "route": "qt",
#         "numbers": phone_no,
#         "message": settings.FAST_2_SMS_TEMPLATE_ID,
#         "variables": "{BB}",
#         "variables_values": "%s" % message
#     }
#     headers = {'cache-control': "no-cache"}
#     response = requests.request("GET", url, headers=headers, params=querystring)
#     print(response.text)
#     return True

# ###################################################################################################3

def format_otp_message(otp):
    return f"Your Verification Code is : {otp}; this will be active for {settings.OTP_EXPIRY / 15} minute(s) - WoodN\'Cart"

def send_otp(phone_no: str, otp):

    querystring = {
        "authorization": settings.FAST_2_SMS_API_KEY,
        # "sender_id": settings.FAST_2_SMS_SENDER_ID,
        # "language": "english",
        "route": "otp",
        "numbers": phone_no,
        # "message": settings.FAST_2_SMS_TEMPLATE_ID,
        # "message": f"{otp} is your OTP to login to ABCHAUZ.COM",
        "variables_values": f"Use {otp} to login at ABCHAUZ"
    }
    headers = {'cache-control': "no-cache"}
    try:
        response = requests.request("GET", FAST_2_SMS_BASE_URL, headers=headers, params=querystring)
        return True
    except Exception as e:
        return False

def send_welcome_message(phone_no):
    # querystring = {
    #     "authorization": settings.FAST_2_SMS_API_KEY,
    #     "sender_id": settings.FAST_2_SMS_SENDER_ID,
    #     "language": "english",
    #     "route": "qt",
    #     "numbers": phone_no,
    #     "message": settings.FAST_2_SMS_TEMPLATE_ID,
    #     "variables": "{AA}",
    #     "variables_values": otp
    # }
    # headers = {'cache-control': "no-cache"}
    # response = requests.request("GET", FAST_2_SMS_BASE_URL, headers=headers, params=querystring)
    # print(response.text)
    # return True
    return


'''
    Sequential模型是多个网络层的线性堆叠
'''
from keras.models import Sequential
from keras.layers import Dense, Activation

'''
    可以通过向Sequential模型传递一个layer的list来构造该模型
'''
model = Sequential([
Dense(32, units=784),
Activation('relu'),
Dense(10),
Activation('softmax'),
])

'''
    也可以通过.add()方法一个个的将layer加入模型中：
'''
model = Sequential()
model.add(Dense(32, input_shape=(784,)))
model.add(Activation('relu'))

'''
    指定输入数据的shape

        模型需要知道输入数据的shape，因此，Sequential的第一层需要接受一个关于输入数据shape的参数
        后面的各个层则可以自动的推导出中间数据的shape，因此不需要为每个层都指定这个参数。

    指定输入数据shape的几种方法

        1.传递一个input_shape的关键字参数给第一层，input_shape是一个tuple类型的数据，其中也可以填入None，如果填入None则表示此位置可能是任何正整数。
        数据的batch大小不应包含在其中。

        2.有些2D层，如Dense，支持通过指定其输入维度input_dim来隐含的指定输入数据shape,是一个Int类型的数据。
        一些3D的时域层支持通过参数input_dim和input_length来指定输入shape。

        3.如果你需要为输入指定一个固定大小的batch_size（常用于stateful RNN网络），可以传递batch_size参数到一个层中，
        例如你想指定输入张量的batch大小是32，数据shape是（6，8），则你需要传递batch_size=32和input_shape=(6,8)。
'''

'''
    编译
        在训练模型之前，我们需要通过compile来对学习过程进行配置。

            优化器optimizer：该参数可指定为已预定义的优化器名，如rmsprop、adagrad，或一个Optimizer类的对象，详情见optimizers

            损失函数loss：该参数为模型试图最小化的目标函数，它可为预定义的损失函数名，如categorical_crossentropy、mse，也可以为一个损失函数。

            指标列表metrics：对分类问题，我们一般将该列表设置为metrics=['accuracy']。
            指标可以是一个预定义指标的名字,也可以是一个用户定制的函数.指标函数应该返回单个张量,或一个完成metric_name - > metric_value映射的字典.
'''
# 1.For a multi-class classification problem
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# 2.For a binary classification problem
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])

# 3.For a mean squared error regression problem
model.compile(optimizer='rmsprop',
              loss='mse')

# 4.For custom metrics
import keras.backend as K

def mean_pred(y_true, y_pred):
    return K.mean(y_pred)

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy', mean_pred])

'''
    训练

    Keras以Numpy数组作为输入数据和标签的数据类型。

    训练模型一般使用fit函数。
'''
# 1.For a single-input model with 2 classes (binary classification):

model = Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])

# Generate dummy data
import numpy as np
data = np.random.random((1000, 100))
labels = np.random.randint(2, size=(1000, 1))

# Train the model, iterating on the data in batches of 32 samples
model.fit(data, labels, epochs=10, batch_size=32)

# 2.For a single-input model with 10 classes (categorical classification):

model = Sequential()
model.add(Dense(32, activation='relu', input_dim=100))
model.add(Dense(10, activation='softmax'))
model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# Generate dummy data
import numpy as np
data = np.random.random((1000, 100))
labels = np.random.randint(10, size=(1000, 1))

# Convert labels to categorical one-hot encoding
one_hot_labels = keras.utils.to_categorical(labels, num_classes=10)

# Train the model, iterating on the data in batches of 32 samples
model.fit(data, one_hot_labels, epochs=10, batch_size=32)


class Solution:
    def knightDialerUtil(self, x: int, y: int, ind: int, n: int) -> int:
        if x > 3 or x < 0 or y < 0 or y > 2 or (x == 3 and y == 0) or (x == 3 and y == 2):
            return 0

        if ind == n:
            return 1

        if self.dp[ind][x][y] != -1:
            return self.dp[ind][x][y]

        ans = 0

        for i in range(8):
            newX = x + self.dx[i]
            newY = y + self.dy[i]
            ans = (ans + self.knightDialerUtil(newX, newY, ind + 1, n)) % self.mod

        self.dp[ind][x][y] = ans

        return ans

    def knightDialer(self, n: int) -> int:
        self.dx = [-2, -1, 1, 2, 2, 1, -1, -2]
        self.dy = [1, 2, 2, 1, -1, -2, -2, -1]
        self.mod = int(1e9 + 7)
        self.dp = [[[-1] * 3 for i in range(4)] for i in range(n + 1)]
        ans = 0

        for i in range(4):
            for j in range(3):
                ans = (ans + self.knightDialerUtil(i, j, 1, n)) % self.mod

        return ans


# !/usr/bin/env python
# -*- coding:utf-8 -*-
# author: Fangyang  time:2017/12/28

# xx:公有变量
# _x：单前置下划线，私有化属性或方法，from somemoudule import * 禁止导入，类对象和子类可以访问
# __xx:双前置下划线，避免与子类中的属性命名冲突，无法在外部直接访问（名字重复所以访问不到）,想访问，_类名__xx(系统不推荐)
# __xx__:双前后下划线，用户名字空间的系统方法或属性
# xx_：单后置下划线，用于避免与python关键词冲突 if_ = 100

class Test(object):
    def __init__(self):
        self.num = 100
        self.__num = 1000

    def setNum(self,newNum):
        self.__num = newNum

    def getNum(self):
        return self.__num

t = Test()
t.num = 200
# print(t.__num)  # 这里带__的属性，不能在类外面使用，报错啊
t.__num = 2000
print(t.num)
print(t.__num)  # 这里可以使用，是因为前面赋值了，相当于添加了一个同名的__num属性

print('-'*50)

print(t.getNum())
t.setNum(50)
print(t.getNum())


import bpy

from mathutils import Matrix, Vector

from . import refresh, flip
from .. import addon, modifier
from ... import topbar

def change(ot, context, event, to='CUT', init=False):
    preference = addon.preference()
    bc = context.window_manager.bc

    if ot.datablock['targets'] or init:
        value = to if init or to != ot.mode else ot.last['mode']

        offset = preference.shape.offset

        was_flip_z = ot.flip_z

        if value == 'MAKE':
            ot.flip_z = False
            offset = 0
        elif value == 'JOIN':
            ot.flip_z = True
            offset = -offset
        else:
            ot.flip_z = False

        matrix = ot.start['matrix'] @ Matrix.Translation(Vector((0, 0, offset)))
        bc.shape.matrix_world.translation = matrix.translation
        bc.plane.matrix_world.translation = matrix.translation
        bc.lattice.matrix_world.translation = matrix.translation

        if not ot.flip_z and was_flip_z or ot.flip_z and not was_flip_z:
            flip.shape(ot, context, event, report=False)

        def store_last(value, to):
            if value != to:
                ot.last['mode'] = value
            else:
                ot.last['mode'] = 'CUT' if value != 'CUT' else value

        store_last(value, to)

        for obj in ot.datablock['targets']:
            for mod in obj.modifiers:
                if mod == modifier.shape_bool(ot, obj) or mod.type == 'BOOLEAN' and not mod.object:
                    obj.modifiers.remove(mod)

        if not init and ot.original_mode == 'EDIT_MESH':
            bpy.ops.object.mode_set(mode='OBJECT')
            for pair in zip(ot.datablock['targets'], ot.datablock['overrides']):
                obj = pair[0]
                override = pair[1]

                name = obj.data.name
                obj.data.name = 'tmp'
                obj.data = override
                obj.data.name = name

            bpy.ops.object.mode_set(mode='EDIT')

        for obj in ot.datablock['slices']:
            bpy.data.objects.remove(obj)

        ot.datablock['slices'] = list()

        ot.mode = value
        topbar.change_prop(context, 'mode', value)

        if not init:

            refresh.shape(ot, context, event)

            ot.report({'INFO'}, '{}{}{}'.format(
                value.title()[:-1 if value in {'SLICE', 'MAKE'} else len(value)] if value != 'KNIFE' else 'Using Knife',
                't' if value in {'CUT', 'INSET'} else '',
                'ing' if value != 'KNIFE' else ''))


# -*- coding:utf-8 -*-
import os
import re
import math

f_stop = []
templine = []
IDF = {}
TF = {}
DF = {}
w = {}
letters = re.compile(r'[a-zA-Z]+-?[a-zA-Z]+')

def get_df(file):

    tdic = {}
    ff = open(file, "rb")
    for line in ff:
        str_line = str(line).strip().lower()
        for word in re.findall(letters, str_line):
            if word not in f_stop:
                if word in tdic:
                    tdic[word] += 1
                else:
                    tdic[word] = 1
    for word in tdic:
        if word in DF:
            DF[word] += 1
        else:
            DF[word] = 1
    return tdic

def get_tf():
    num_doc = 0
    root_1dir = r"20news-18828"
    for pack in os.listdir(root_1dir):
        root_dir = root_1dir + "\\" + pack
        for file in os.listdir(root_dir):
            file_name = root_dir + "\\" + file
            num_doc += 1
            cword = get_df(file_name)
            for k in cword:
                if cword[k] > 0:
                    TF[k, pack + "\\" + file] = 1.0 + math.log(cword[k])
                else:
                    TF[k, pack + "\\" + file] = 0
    return num_doc

def get_idf():
    N = get_tf()
    for word in DF:
        IDF[word] = math.log(N / DF[word])

def get_w():
    for k1, k2 in TF:
        w[k1, k2] = TF[k1, k2] * IDF[k1]
        templine.append("%s\t%s\t%s\n" % (k1, k2, w[k1, k2]))
    with open('vsm.txt', 'w+') as file:
        file.writelines(templine)

def get_stop():
    ff_stop = open("stopword", "rb")
    for stop_line in ff_stop:
        str_stop = str(stop_line).strip()
        for sword in re.findall(letters, str_stop):
            f_stop.append(sword)

get_stop()
get_idf()
get_w()


try:
    import spacy
except:
    import spacy
from negspacy.negation import Negex
#!pip install spacy
#!python3 -m spacy download en_core_web_sm

nlp = spacy.load("en_core_web_sm")
nlp.add_pipe("negex", config={"ent_types":["PERSON","ORG"]})

doc = nlp("Aren’t you coming? Doesn’t he understand? Are you not coming? Does he not understand? Which of the following did not occur? Does Jeff is a real person? Name something David hadn't reveal in his vacation?")
#doc = nlp(" Which of the following did not occur?")
for e in doc.ents:
    print(e.text, e._.negex)

for token in doc:
    print(token.text, token.lemma_, token.pos_, token.tag_, token.dep_,
            token.shape_, token.is_alpha, token.is_stop)

print(f"NEG:{any([token.dep_=='neg' for token in doc])}")
print('='*20)
for chunk in doc.noun_chunks:
    print(chunk.text, chunk.root.text, chunk.root.dep_,
            chunk.root.head.text)
print('='*20)
for token in doc:
    print(token.text, token.dep_, token.head.text, token.head.pos_,[child for child in token.children])
import spacy
from spacy import displacy

text = "When Sebastian Thrun started working on self-driving cars at Google in 2007, few people outside of the company took him seriously."

nlp = spacy.load("en_core_web_sm")
doc = nlp(text)
displacy.serve(doc, style="ent")


from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks

inference_pipeline = pipeline(
    task=Tasks.auto_speech_recognition,
    model='NPU-ASLP/speech_mfcca_asr-zh-cn-16k-alimeeting-vocab4950',
    model_revision='v3.0.0'
)

rec_result = inference_pipeline(audio_in='https://isv-data.oss-cn-hangzhou.aliyuncs.com/ics/MaaS/ASR/test_audio/asr_example_zh.wav')
print(rec_result)


import pandas as pd
import yfinance as yf
import numpy as np
import requests
import json
import os

def CreateCsvFile():

    path = os.path.dirname(os.path.abspath(__file__))

    df = yf.download('btc-usd')
    df = df.drop(['Open', 'Adj Close', 'High', 'Low'], axis=1)
    df['Close'] = df.Close.round(decimals=2)
    df.index.name = 'timestamp'
    datefilter = df.index.min()
    df2 = pd.read_csv(f'{path}/bitcoin_history.csv')
    df2 = df2.drop(['open', 'high', 'low'], axis=1)
    df2['close'] = df2.close.round(decimals=2)
    df2.rename(columns = {'close' : 'Close'}, inplace=True)
    df2['time'] = pd.to_datetime(df2['time'], format='%Y/%m/%d')
    df2.set_index(['time'],inplace=True)
    df2.index.name = 'timestamp'
    df2 = df2.loc[(df2.index >= df2.index.min()) & (df2.index < datefilter)]
    df = pd.concat([df2, df], axis=0)

    for n in [20, 50, 100, 200, 350, 700, 1400]:
        df[f'{n} MA'] = df.Close.rolling(n).mean().round(decimals=2)
    df['200 Weeks Index'] = df['Close'] / df['1400 MA']
    df.rename(columns = {'1400 MA':'200 Weeks MA'}, inplace = True)
    df['2 Years Buy MA'] = df.Close.rolling(730).mean().round(decimals=2)
    df['2 Years Sell MA'] =  (df['2 Years Buy MA'] * 5).round(decimals=2)
    volatilidad = df["Close"].rolling(20).std()
    df['boll inf'] = (df['20 MA'] - 2 * volatilidad).round(decimals=2)
    df['boll sup'] = (df['20 MA'] + 2 * volatilidad).round(decimals=2)
    df['Pi Cycle 1'] = df.Close.rolling(111).mean().round(decimals=2)
    df['Pi Cycle 2'] = df['350 MA'] * 2

    url = 'https://api.alternative.me/fng/?limit=0'
    response = requests.get(url)
    df_fng = pd.DataFrame(response.json()['data'])
    df_fng['value'] = df_fng['value'].astype(int)
    df_fng['timestamp'] = pd.to_datetime(df_fng['timestamp'], unit='s')
    df_fng['Date'] = df_fng['timestamp']
    df_fng.set_index(['timestamp'],inplace=True)
    df_fng.drop(columns=['time_until_update'],inplace=True)

    df_fng.rename(columns = {'value' : 'F&G_Value'}, inplace=True)
    df_fng.rename(columns = {'value_classification' : 'F&G_Class'}, inplace=True)
    df_fng.index.name = 'timestamp'

    BTC = pd.merge(left=df, right=df_fng, how='left', left_on='timestamp', right_on='timestamp')
    BTC['timestamp'] = BTC.index

    BTC['MA Index _ 100 MA'] = BTC['100 MA'] / BTC['700 MA']
    BTC['MA Index _ 200 MA'] = BTC['200 MA'] / BTC['700 MA']
    BTC['MA Index _ MA Down'] = 1
    BTC['MA Index _ MA Bottom'] = BTC['MA Index _ 100 MA'].min()

    BTC['200 Weeks Index _ 200 Weeks Bottom'] = 1
    BTC['200 Weeks Index _ 200 Weeks Down'] = BTC['200 Weeks Index'].min()+BTC['200 Weeks Index'].max()*0.075
    BTC['200 Weeks Index _ Price'] = np.where(BTC['200 Weeks Index']>7.5,7.5,BTC['200 Weeks Index'])

    BTC['Pi Cycle Index _ Index'] = BTC['Pi Cycle 1'] / BTC['Pi Cycle 2']
    BTC['Pi Cycle Index _ Down'] = BTC['Pi Cycle Index _ Index'].min()+BTC['Pi Cycle Index _ Index'].max()*0.125
    BTC['Pi Cycle Index _ Top'] = 1
    BTC['Pi Cycle Index _ Bottom'] = BTC['Pi Cycle Index _ Index'].min()

    BTC['2 Years Index _ Index'] = BTC['Close'] / BTC['2 Years Buy MA']
    BTC['2 Years Index _ Down'] = 1
    BTC['2 Years Index _ Top'] = 5
    BTC['2 Years Index _ Bottom'] = BTC['2 Years Index _ Index'].min()
    max = BTC['2 Years Index _ Top']*2
    BTC['2 Years Index _ Index'] = np.where(BTC["2 Years Index _ Index"] > max,max,BTC["2 Years Index _ Index"])

    # Open the JSON file in read mode and load it into a Python object
    with open('parameters.json', 'r') as f:
        data = json.load(f)
    file_name = data['name_file']

    #Generate file whit reewrite mode
    BTC.to_csv(f'{path}/{file_name}', index=False, sep=',', mode='w')

    return "OK"


import asyncio
import discord
import random
import datetime
import subprocess
import re
from   discord.ext import commands
from   Cogs import Settings
from   Cogs import DisplayName
from   Cogs import Nullify
from   Cogs import downloader
import youtube_dl
import functools

if not discord.opus.is_loaded():
    # the 'opus' library here is opus.dll on windows
    # or libopus.so on linux in the current directory
    # you should replace this with the location the
    # opus library is located in and with the proper filename.
    # note that on windows this DLL is automatically provided for you
    discord.opus.load_opus('opus')

class Example:

    def __init__(self, bot, settings):
        self.bot = bot
        self.settings = settings

    @commands.command()
    async def add(self, ctx, left : int, right : int):
        """Adds two numbers together."""
        await ctx.channel.send(left + right)

    @commands.command()
    async def roll(self, ctx, dice : str):
        """Rolls a dice in NdN±N format."""
        try:
            parts = dice.split('d')
            rolls = int(parts[0])
            limit = parts[1]
            add   = 0
            if "-" in limit:
                parts = limit.split('-')
                limit = int(parts[0])
                add = int(parts[1])*-1
            elif "+" in limit:
                parts = limit.split('+')
                limit = int(parts[0])
                add = int(parts[1])
            else:
                limit = int(limit)
        except Exception:
            await ctx.channel.send('Format has to be in NdN±N!')
            return
        numbers = []
        number_sum = 0
        for r in range(rolls):
            roll = random.randint(1, limit)
            number_sum += roll
            numbers.append(str(roll))

        number_string = ", ".join(numbers)
        number_string = "```\n= Dice Rolls ========================\n" + number_string
        if not add == 0:
            sign = "+"
            if add < 0:
                sign = ""
            number_string += "\n\n= Pre-Total =========================\n{}".format(number_sum)
            number_string += "\n\n= Modifier ==========================\n{}{}".format(sign, add)

        number_string += "\n\n= Final Total =======================\n{}```".format(number_sum + add)
        await ctx.channel.send(number_string)

    @commands.command(description='For when you wanna settle the score some other way')
    async def choose(self, ctx, *choices : str):
        """Chooses between multiple choices."""
        msg = random.choice(choices)
        msg = Nullify.clean(msg)
        await ctx.channel.send(msg)

    @commands.command(pass_context=True)
    async def joined(self, ctx, *, member : str = None):
        """Says when a member joined."""

        # Check if we're suppressing @here and @everyone mentions
        if self.settings.getServerStat(ctx.message.guild, "SuppressMentions").lower() == "yes":
            suppress = True
        else:
            suppress = False

        if member is None:
            member = ctx.message.author

        if type(member) is str:
            memberName = member
            member = DisplayName.memberForName(memberName, ctx.message.guild)
            if not member:
                msg = 'I couldn\'t find *{}*...'.format(memberName)
                # Check for suppress
                if suppress:
                    msg = Nullify.clean(msg)
                await ctx.channel.send(msg)
                return

        await ctx.channel.send('*{}* joined *{}*'.format(DisplayName.name(member), member.joined_at.strftime("%Y-%m-%d %I:%M %p")))

class VoiceEntry:
    def __init__(self, message, player, title, duration, ctx):
        self.requester = message.author
        self.channel = message.channel
        self.player = player
        self.title = title
        self.duration = duration
        self.ctx = ctx

    def __str__(self):
        fmt = '*{}* requested by *{}*'.format(self.title, DisplayName.name(self.requester))
        seconds = self.duration
        if seconds:
            hours = seconds // 3600
            minutes = (seconds % 3600) // 60
            seconds = seconds % 60
            fmt = fmt + ' [length: {:02d}h:{:02d}m:{:02d}s]'.format(round(hours), round(minutes), round(seconds))
        return fmt

class VoiceState:
    def __init__(self, bot, settings):
        self.current = None
        self.voice = None
        self.bot = bot
        self.play_next_song = asyncio.Event()
        self.playlist = []
        self.repeat = False
        self.votes = []
        self.audio_player = self.bot.loop.create_task(self.audio_player_task())
        self.start_time = datetime.datetime.now()
        self.total_playing_time = datetime.datetime.now() - datetime.datetime.now()
        self.is_paused = False
        self.settings = settings

    def is_playing(self):
        if self.voice is None or self.current is None:
            return False

        player = self.voice
        return not player.is_paused() and player.is_playing()

    @property
    def player(self):
        return self.current.player

    def skip(self):
        self.votes = []
        if self.voice.is_playing():
            self.voice.stop()

    def toggle_next(self, error):
        if error:
            print("Error and shit... Should probably handle this one day.")
        self.bot.loop.call_soon_threadsafe(self.play_next_song.set)

    async def audio_player_task(self):
        while True:

            self.play_next_song.clear()

            if len(self.playlist) <= 0:
                await asyncio.sleep(1)
                continue

            self.start_time = datetime.datetime.now()
            self.current = await self.create_youtube_entry(self.playlist[0]["ctx"], self.playlist[0]["raw_song"], self.playlist[0]['song'], self.playlist[0]['duration'])

            #Check if youtube-dl found the song
            if self.current == False:
                del self.playlist[0]
                continue

            seconds = self.playlist[0]["duration"]
            hours = seconds // 3600
            minutes = (seconds % 3600) // 60
            seconds = seconds % 60

            self.votes = []
            self.votes.append({ 'user' : self.current.requester, 'value' : 'keep' })
            await self.current.channel.send('Now playing *{}* - [{:02d}h:{:02d}m:{:02d}s] - requested by *{}*'.format(self.playlist[0]["song"], round(hours), round(minutes), round(seconds), DisplayName.name(self.playlist[0]['requester'])))

            await self.play_next_song.wait()
            self.total_playing_time = datetime.datetime.now() - datetime.datetime.now()
            if self.repeat:
                self.playlist.append(self.playlist[0])
            del self.playlist[0]

    async def create_youtube_entry(self, ctx, song: str, title: str, duration):

        opts = {
            'buffersize': '20000000',
            'f': 'bestaudio',
            'default_search': 'auto',
            'quiet': True
        }
        volume = self.settings.getServerStat(ctx.message.guild, "Volume")
        defVolume = self.settings.getServerStat(ctx.message.guild, "DefaultVolume")
        if volume:
            volume = float(volume)
        else:
            if defVolume:
                volume = float(self.settings.getServerStat(ctx.message.guild, "DefaultVolume"))
            else:
                # No volume or default volume in settings - go with 60%
                volume = 0.6

        try:

            # Create a rewrite player because why not...
            # PS - Look at all these shitty attempts?!
            #
            # audioProc = subprocess.Popen( [ "youtube-dl", "-q", "-o", "-", song ], stdout=subprocess.PIPE )
            # before_args = "-reconnect_streamed 1"
            # audioProc = subprocess.Popen( "youtube-dl -o - \"" + song + "\"", shell=True, stdout=subprocess.PIPE )
            # ffsource = discord.FFmpegPCMAudio(audioProc.stdout, before_options=before_args, pipe=True)
            # audioProc = subprocess.Popen( "youtube-dl -o - \"" + song + "\" | ffmpeg -i pipe:0 -ac 2 -f s16le -ar 48000 pipe:1 -reconnect_streamed 1", stdout=subprocess.PIPE, shell=True )
            #
            # VICTORY!
            #
            audioProc = subprocess.Popen( "ffmpeg -hide_banner -loglevel error -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 2 -i \"" + song + "\" -ac 2 -f s16le -ar 48000 pipe:1", stdout=subprocess.PIPE, shell=True )
            rawAudio = discord.PCMAudio(audioProc.stdout)
            volumeSource = discord.PCMVolumeTransformer(rawAudio)
            #
            # ffsource = discord.FFmpegPCMAudio(song, before_options=before_args, pipe=True)
            # volumeSource = discord.PCMVolumeTransformer(ffsource)
            self.voice.play(volumeSource, after=self.toggle_next)

        except Exception as e:
            fmt = 'An error occurred while processing this request: ```py\n{}: {}\n```'.format(type(e).__name__, e)
            await ctx.channel.send(fmt)
            return False
        else:
            #self.voice.volume = volume
            self.voice.source.volume = volume
            entry = VoiceEntry(ctx.message, self.voice, title, duration, ctx)
            return entry

class Music:
    """Voice related commands.

    Works in multiple servers at once.
    """
    def __init__(self, bot, settings):
        self.bot = bot
        self.voice_states = {}
        self.settings = settings
        self.downloader = downloader.Downloader()
        # Regex for extracting urls from strings
        self.regex = re.compile(r"(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?")

    async def onready(self):
        # Clear any previous playlist settings
        for guild in self.bot.guilds:
            self.settings.setServerStat(guild, "Playlisting", None)
            self.settings.setServerStat(guild, "PlaylistRequestor", None)

    def get_voice_state(self, server):
        state = self.voice_states.get(server.id)
        if state is None:
            state = VoiceState(self.bot, self.settings)
            self.voice_states[server.id] = state

        return state

    async def create_voice_client(self, channel):
        voice = await channel.connect()
        state = self.get_voice_state(channel.guild)
        state.voice = voice

    def __unload(self):
        for state in self.voice_states.values():
            try:
                state.audio_player.cancel()
                if state.voice:
                    self.bot.loop.create_task(state.voice.disconnect())
            except:
                pass

    async def _user_in_voice(self, ctx):
        # Check if we're in a voice channel
        # voiceChannel = self.bot.voice_client_in(ctx.message.guild)
        voiceChannel = None
        for client in self.bot.voice_clients:
            if client.guild == ctx.guild:
                # Found it?
                voiceChannel = client.channel

        if not voiceChannel:
            # We're not in a voice channel
            return None

        channel = ctx.message.channel
        author  = ctx.message.author
        server  = ctx.message.guild

        # Check if user is admin
        isAdmin = ctx.message.author.permissions_in(ctx.message.channel).administrator
        if not isAdmin:
            checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
            for role in ctx.message.author.roles:
                for aRole in checkAdmin:
                    # Get the role that corresponds to the id
                    if str(aRole['ID']) == str(role.id):
                        isAdmin = True
        if isAdmin:
            return True

        # Here, user is not admin - make sure they're in the voice channel
        # Check if the user in question is in a voice channel
        if ctx.message.author in voiceChannel.members:
            return True
        # If we're here - we're not admin, and not in the same channel, deny
        return False

    @commands.command(pass_context=True, no_pm=True)
    async def pmax(self, ctx, *, max_songs = None):
        """Sets the maximum number of songs to load from a playlist (owner only).
        The higher the number, the long it takes to load (-1 to load all).
        Default is 25."""

        # Only allow owner
        isOwner = self.settings.isOwner(ctx.author)
        if isOwner == None:
            msg = 'I have not been claimed, *yet*.'
            await ctx.channel.send(msg)
            return
        elif isOwner == False:
            msg = 'You are not the *true* owner of me.  Only the rightful owner can use this command.'
            await ctx.channel.send(msg)
            return

        if max_songs == None:
            lev = 25
            try:
                lev = self.settings.serverDict['PlistMax']
            except KeyError:
                pass
            if lev == -1:
                await ctx.channel.send("The current playlist max is set to: *All songs*")
            elif lev == 1:
                await ctx.channel.send("The current playlist max is set to: *1 song*")
            else:
                await ctx.channel.send("The current playlist max is set to: *{} songs*".format(lev))
        else:
            try:
                max_songs = int(max_songs)
            except Exception:
                await ctx.channel.send("Playlist max must be an integer.")
                return
            if max_songs < -1:
                max_songs = -1
            self.settings.serverDict['PlistMax'] = max_songs
            if max_songs == -1:
                await ctx.channel.send("The playlist max is now set to: *All songs*")
            elif max_songs == 1:
                await ctx.channel.send("The playlist max is now set to: *1 song*")
            else:
                await ctx.channel.send("The playlist max is now set to: *{} songs*".format(max_songs))

    @commands.command(pass_context=True, no_pm=True)
    async def pdelay(self, ctx, *, delay = None):
        """Sets the delay in seconds between loading songs in playlist (owner only).
        Lower delay may result in Youtube block - default is 3."""

        # Only allow owner
        isOwner = self.settings.isOwner(ctx.author)
        if isOwner == None:
            msg = 'I have not been claimed, *yet*.'
            await ctx.channel.send(msg)
            return
        elif isOwner == False:
            msg = 'You are not the *true* owner of me.  Only the rightful owner can use this command.'
            await ctx.channel.send(msg)
            return

        if delay == None:
            lev = 3
            try:
                lev = self.settings.serverDict['PlistDelay']
            except KeyError:
                pass
            if lev == 1:
                await ctx.channel.send("The current playlist load delay is set to: *1 second*")
            else:
                await ctx.channel.send("The current playlist load delay is set to: *{} seconds*".format(lev))
        else:
            try:
                delay = int(delay)
            except Exception:
                await ctx.channel.send("Delay must be an integer.")
                return
            if delay < 0:
                delay = 0
            self.settings.serverDict['PlistDelay'] = delay
            if delay == 1:
                await ctx.channel.send("The playlist load delay is now set to: *1 second*")
            else:
                await ctx.channel.send("The playlist load delay is now set to: *{} seconds*".format(delay))

    @commands.command(pass_context=True, no_pm=True)
    async def plevel(self, ctx, *, level = None):
        """Sets the access level for playlists (owner only):
        0 = Everyone
        1 = Bot Admins and up
        2 = Admins and up
        3 = Owner
        4 = Disabled (default)"""

        # Only allow owner
        isOwner = self.settings.isOwner(ctx.author)
        if isOwner == None:
            msg = 'I have not been claimed, *yet*.'
            await ctx.channel.send(msg)
            return
        elif isOwner == False:
            msg = 'You are not the *true* owner of me.  Only the rightful owner can use this command.'
            await ctx.channel.send(msg)
            return

        if level == None:
            # Get the current level
            lev = 4
            pword = "Disabled"
            try:
                lev = self.settings.serverDict['PlistLevel']
            except KeyError:
                pass

            if lev == 0:
                pword = "Everyone"
            elif lev == 1:
                pword = "Bot Admins"
            elif lev == 2:
                pword = "Admins"
            elif lev == 3:
                pword = "Owner"
            await ctx.channel.send("The current playlist level is set to: *{} ({})*".format(pword, lev))
        else:
            try:
                level = int(level)
            except Exception:
                await ctx.channel.send("Level must be an integer from 0 to 4.")
                return
            if level < 0 or level > 4:
                await ctx.channel.send("Level must be an integer from 0 to 4.")
                return
            pword = "Disabled"
            if level == 0:
                pword = "Everyone"
            elif level == 1:
                pword = "Bot Admins"
            elif level == 2:
                pword = "Admins"
            elif level == 3:
                pword = "Owner"
            self.settings.serverDict['PlistLevel'] = level
            await ctx.channel.send("Playlist level is now set to: *{} ({})*".format(pword, level))

    @commands.command(pass_context=True, no_pm=True)
    async def pskip(self, ctx):
        """Skips loading the rest of a playlist - can only be done by the requestor, or bot-admin/admin."""

        try:
            playlisting = self.settings.getServerStat(ctx.guild, "Playlisting")
            requestor   = self.settings.getServerStat(ctx.guild, "PlaylistRequestor")
        except Exception:
            playlisting = None
            requestor   = None

        if playlisting == None:
            await ctx.channel.send("I'm not currently adding a playlist.")
            return

        # Check requestor id - and see if we have it
        if not ctx.author.id == requestor:
            #admin/bot-admin only
            isAdmin = ctx.message.author.permissions_in(ctx.message.channel).administrator
            if not isAdmin:
                checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
                for role in ctx.message.author.roles:
                    for aRole in checkAdmin:
                        # Get the role that corresponds to the id
                        if str(aRole['ID']) == str(role.id):
                            isAdmin = True
            if not isAdmin:
                await ctx.channel.send('You do not have sufficient privileges to access this command.')
                return

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return
        elif userInVoice == None:
            await ctx.channel.send('I\'m not in a voice channel.  Use the `{}summon`, `{}join [channel]` or `{}play [song]` commands to start playing something.'.format(ctx.prefix, ctx.prefix, ctx.prefix))
            return

        # At this point - we *should* have everything we need to cancel - so do it
        self.settings.setServerStat(ctx.guild, "Playlisting", None)
        self.settings.setServerStat(ctx.guild, "PlaylistRequestor", None)

        await ctx.send("Playlist loading canceled!")

    @commands.command(pass_context=True, no_pm=True)
    async def playingin(self, ctx):
        """Shows the number of servers the bot is currently playing music in."""
        playing_in = 0
        for serv in self.bot.guilds:
            state = self.get_voice_state(serv)
            if state.voice and state.voice.is_playing():
                playing_in += 1

        if len(self.bot.guilds) == 1:
            msg = "Playing music in {} of {} server.".format(playing_in, len(self.bot.guilds))
        else:
            msg = "Playing music in {} of {} servers.".format(playing_in, len(self.bot.guilds))

        await ctx.channel.send(msg)

    @commands.command(pass_context=True, no_pm=True)
    async def join(self, ctx, *, channel : str = None):
        """Joins a voice channel."""
        # No channel sent
        if channel == None:
            await ctx.channel.send("Usage: `{}join [channel name]`".format(ctx.prefix))
            return

        found_channel = None
        for c in ctx.guild.channels:
            # Go through our channels, look for VoiceChannels,
            # and compare names
            if not type(c) is discord.VoiceChannel:
                continue
            # Check name case-insensitive
            if c.name.lower() == channel.lower():
                # Found it!
                found_channel = c
                break

        if found_channel == None:
            # We didn't find it...
            await ctx.channel.send("I couldn't find that voice channel...")
            return

        # At this point - we have a channel - set channel to found_channel
        channel = found_channel

        try:
            await self.create_voice_client(channel)
        except discord.ClientException:
            await ctx.channel.send('Already in a voice channel...')
        except discord.InvalidArgument:
            await ctx.channel.send('This is not a voice channel...')
        else:
            await ctx.channel.send('Ready to play audio in ' + channel.name)

    @commands.command(pass_context=True, no_pm=True)
    async def summon(self, ctx):
        """Summons the bot to join your voice channel."""

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return

        state = self.get_voice_state(ctx.message.guild)

        if state.is_playing():
            await ctx.channel.send('I\`m already playing in a channel, Join me there instead! :D')
            return

        if ctx.message.author.voice is None:
            await ctx.channel.send('You are not in a voice channel.')
            return False
        summoned_channel = ctx.message.author.voice.channel

        if state.voice is None:
            state.voice = await summoned_channel.connect() # self.bot.join_voice_channel(summoned_channel)
        else:
            await state.voice.move_to(summoned_channel)

        return True

    @commands.command(pass_context=True, no_pm=True)
    async def play(self, ctx, *, song : str = None):
        """Plays a song.

        If there is a song currently in the queue, then it is
        queued until the next song is done playing.

        This command automatically searches as well from YouTube.
        The list of supported sites can be found here:
        https://rg3.github.io/youtube-dl/supportedsites.html
        """

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return

        if song == None:
            await ctx.channel.send('Sweet.  I will *totally* add nothing to my list.  Thanks for the *superb* musical suggestion...')
            return

        state = self.get_voice_state(ctx.message.guild)

        if state.voice is None:
            success = await ctx.invoke(self.summon)
            if not success:
                return
        else:
            # Check if we're already adding a playlist
            try:
                playlisting = self.settings.getServerStat(ctx.guild, "Playlisting")
            except Exception:
                playlisting = None
            if playlisting:
                await ctx.channel.send("I'm currently importing a playlist - please wait for that to finish before enqueuing more songs.")
                return

        #await state.songs.put(entry)

        opts = {
            'f': 'bestaudio',
            'default_search': 'auto',
            'quiet': True
        }

        song = song.strip('<>')

        # Check if url - if not, remove /
        matches = re.finditer(self.regex, song)
        matches = list(matches)
        if not len(matches):
            song = song.replace('/', '')

        #info = await self.bot.loop.run_in_executor(None, func)

        # First we check for our permission level
        author_perms = 0
        checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
        for role in ctx.author.roles:
            for aRole in checkAdmin:
                # Get the role that corresponds to the id
                if str(aRole['ID']) == str(role.id):
                    author_perms = 1
        if ctx.message.author.permissions_in(ctx.message.channel).administrator:
            author_perms = 2
        if self.settings.isOwner(ctx.author):
            author_perms = 3

        # Get server info
        playlist_level = 4
        try:
            playlist_level = self.settings.serverDict['PlistLevel']
        except KeyError:
            pass
        playlist_max = 25
        try:
            playlist_max = self.settings.serverDict['PlistMax']
        except KeyError:
            pass
        playlist_delay = 3
        try:
            playlist_delay = self.settings.serverDict['PlistDelay']
        except KeyError:
            pass

        if author_perms >= playlist_level:
            plist = True
        else:
            plist = False
        info = await self.downloader.extract_info(
                self.bot.loop,
                song,
                playlist=plist,
                download=False,
                process=False
            )

        if info.get('url', '').startswith('ytsearch'):
            info = await self.downloader.extract_info(
                self.bot.loop,
                song,
                download=False,
                process=True,    # ASYNC LAMBDAS WHEN
                retry_on_error=True,
                playlist=False
            )
            if not info:
                return
            if not all(info.get('entries', [])):
                # empty list, no data
                return
            song = info['entries'][0]['webpage_url']
            info = await self.downloader.extract_info(self.bot.loop, song, download=False, process=False)

        if "entries" in info:
            # Multiple songs - let's add what we need
            #
            if author_perms >= playlist_level:
                # We can add up to playlist_max
                entries_added = 0
                entries_skipped = 0

                mess = await ctx.channel.send("Adding songs from playlist...")

                entries = info['entries']
                entries = list(entries)

                # Get the dropped song's positionn in the playlist
                index = 0
                found = False
                for e in entries:
                    if not e.get('ie_key', '').lower() == 'youtube':
                        index += 1
                        continue
                    eurl = e.get('url')
                    if "v="+eurl in info['webpage_url']:
                        # We found it!
                        found = True
                        break
                    index += 1

                if not found:
                    index = 0

                if playlist_max > -1:
                    if len(entries) - index > playlist_max:
                        total_songs = playlist_max
                    else:
                        total_songs = len(entries) - index
                else:
                    total_songs = len(entries) - index

                # Lock our playlisting
                self.settings.setServerStat(ctx.guild, "Playlisting", True)
                # Add requestor's id
                self.settings.setServerStat(ctx.guild, "PlaylistRequestor", ctx.author.id)

                checkIndex = 0
                for entry in entries:
                    # Start with the song that was dropped
                    if checkIndex < index:
                        checkIndex += 1
                        continue
                    # Increment our count
                    entries_added += 1
                    if not entry.get('ie_key', '').lower() == 'youtube':
                        entries_skipped += 1
                        continue

                    # Edit our status message
                    await mess.edit(content="Enqueuing song {} of {} from *{}* ({} skipped)...".format(entries_added, total_songs, info['title'], entries_skipped))

                    # Create a new video url and get info
                    new_url = "https://youtube.com/v/" + entry.get('url', '')
                    try:
                        entry = await self.downloader.extract_info(
                            self.bot.loop,
                            new_url,
                            download=False,
                            process=True,    # ASYNC LAMBDAS WHEN
                            retry_on_error=True,
                            playlist=False
                        )
                    except Exception:
                        entries_skipped += 1
                        continue
                    if entry == None:
                        entries_skipped += 1
                        continue

                    # Get duration info
                    seconds = entry.get('duration')
                    hours = seconds // 3600
                    minutes = (seconds % 3600) // 60
                    seconds = seconds % 60

                    # Add the song
                    state.playlist.append({ 'song': entry.get('title'), 'duration': entry.get('duration'), 'ctx': ctx, 'requester': ctx.message.author, 'raw_song': entry['formats'][len(entry['formats'])-1]['url']})

                    # Check if we're out of bounds
                    if playlist_max > -1 and entries_added >= playlist_max:
                        break
                    # Try not to get blocked :/
                    await asyncio.sleep(playlist_delay)
                    # Check if we're still playing
                    state = self.get_voice_state(ctx.message.guild)
                    if state.voice == None or self.settings.getServerStat(ctx.guild, "Playlisting") == None:
                        if entries_added == 1:
                            await mess.edit(content="*{}* Cancelled - *1* song loaded.".format(info['title']))
                        else:
                            await mess.edit(content="*{}* Cancelled - *{}* songs loaded.".format(info['title'], entries_added))
                        return
                # Unlock our playlisting
                self.settings.setServerStat(ctx.guild, "Playlisting", None)
                self.settings.setServerStat(ctx.guild, "PlaylistRequestor", None)

                await mess.edit(content=" ")
                if entries_added-entries_skipped == 1:
                    await ctx.channel.send("Enqueued *{}* song from *{}* - (*{}* skipped)".format(entries_added-entries_skipped, info['title'], entries_skipped))
                else:
                    await ctx.channel.send("Enqueued *{}* songs from *{}* - (*{}* skipped)".format(entries_added-entries_skipped, info['title'], entries_skipped))
                return
            else:
                # We don't have enough perms
                entries = info['entries']
                entries = list(entries)
                entry = entries[0]
                if entry.get('ie_key', '').lower() == 'youtube':
                    # Create a new video url and get info
                    new_url = "https://youtube.com/v/" + entry.get('url', '')
                    try:
                        entry = await self.downloader.extract_info(
                            self.bot.loop,
                            new_url,
                            download=False,
                            process=True,    # ASYNC LAMBDAS WHEN
                            retry_on_error=True,
                            playlist=False
                        )
                    except Exception:
                        entry = None
                        pass
                    info = entry
                else:
                    info = None

                if info == None:
                    await ctx.send("I couldn't load that song :(")
                    return

        seconds = info.get('duration', 0)
        hours = seconds // 3600
        minutes = (seconds % 3600) // 60
        seconds = seconds % 60

        # state.playlist.append({ 'song': info.get('title'), 'duration': info.get('duration'), 'ctx': ctx, 'requester': ctx.message.author, 'raw_song': song})
        state.playlist.append({ 'song': info.get('title'), 'duration': info.get('duration'), 'ctx': ctx, 'requester': ctx.message.author, 'raw_song': info['formats'][len(info['formats'])-1]['url']})
        await ctx.channel.send('Enqueued - *{}* - [{:02d}h:{:02d}m:{:02d}s] - requested by *{}*'.format(info.get('title'), round(hours), round(minutes), round(seconds), DisplayName.name(ctx.message.author)))

    @commands.command(pass_context=True, no_pm=True)
    async def repeat(self, ctx, *, repeat = None):
        """Checks or sets whether to repeat or not."""
        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return

        state = self.get_voice_state(ctx.message.guild)

        if repeat == None:
            # Just checking
            if state.repeat:
                await ctx.channel.send('Repeat is currently **on**.')
            else:
                await ctx.channel.send('Repeat is currently **off**.')
            return
        elif repeat.lower() == "on" or repeat.lower() == "yes" or repeat.lower() == "true":
            # Trying to enable repeat
            if state.repeat:
                await ctx.channel.send('Repeat will remain **on**.')
            else:
                state.repeat = True
                await ctx.channel.send('Repeat is now **on**.')
            return
        elif repeat.lower() == "off" or repeat.lower() == "no" or repeat.lower() == "false":
            # Trying to disable repeat
            if not state.repeat:
                await ctx.channel.send('Repeat will remain **off**.')
            else:
                state.repeat = False
                await ctx.channel.send('Repeat is now **off**.')
            return
        else:
            # No working variable - let's just output repeat status
            if state.repeat:
                await ctx.channel.send('Repeat is currently **on**.')
            else:
                await ctx.channel.send('Repeat is currently **off**.')
            return

    @commands.command(pass_context=True, no_pm=True)
    async def willrepeat(self, ctx):
        """Displays whether or not repeat is active."""
        # Check user credentials
        state = self.get_voice_state(ctx.message.guild)
        if state.repeat:
            await ctx.channel.send('Repeat is currently **on**.')
        else:
            await ctx.channel.send('Repeat is currently **off**.')

    @commands.command(pass_context=True, no_pm=True)
    async def volume(self, ctx, value = None):
        """Sets the volume of the currently playing song."""

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return
        elif userInVoice == None:
            await ctx.channel.send('I\'m not in a voice channel.  Use the `{}summon`, `{}join [channel]` or `{}play [song]` commands to start playing something.'.format(ctx.prefix, ctx.prefix, ctx.prefix))
            return

        if not value == None:
            # We have a value, let's make sure it's valid
            try:
                value = int(value)
            except Exception:
                await ctx.channel.send('Volume must be an integer.')
                return

        state = self.get_voice_state(ctx.message.guild)
        if state.is_playing():
            player = state.voice
            if value == None:
                # No value - output current volume
                await ctx.channel.send('Current volume is {:.0%}'.format(player.source.volume))
                return
            if value < 0:
                value = 0
            if value > 100:
                value = 100
            player.source.volume = value / 100
            self.settings.setServerStat(ctx.message.guild, "Volume", player.source.volume)
            await ctx.channel.send('Set the volume to {:.0%}'.format(player.source.volume))
        else:
            # Not playing anything
            await ctx.channel.send('Not playing anything right now...')
            return

    @commands.command(pass_context=True, no_pm=True)
    async def pause(self, ctx):
        """Pauses the currently played song."""

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return
        elif userInVoice == None:
            await ctx.channel.send('I\'m not in a voice channel.  Use the `{}summon`, `{}join [channel]` or `{}play [song]` commands to start playing something.'.format(ctx.prefix, ctx.prefix, ctx.prefix))
            return

        state = self.get_voice_state(ctx.message.guild)
        if state.voice.is_playing():
            player = state.voice
            player.pause()
            state.total_playing_time += (datetime.datetime.now() - state.start_time)
            state.is_paused = True

    @commands.command(pass_context=True, no_pm=True)
    async def resume(self, ctx):
        """Resumes the currently played song."""

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return
        elif userInVoice == None:
            await ctx.channel.send('I\'m not in a voice channel.  Use the `{}summon`, `{}join [channel]` or `{}play [song]` commands to start playing something.'.format(ctx.prefix, ctx.prefix, ctx.prefix))
            return

        state = self.get_voice_state(ctx.message.guild)
        if state.voice.is_paused():
            player = state.voice
            player.resume()
            state.start_time = datetime.datetime.now()
            state.is_paused = False

    @commands.command(pass_context=True, no_pm=True)
    async def stop(self, ctx):
        """Stops playing audio and leaves the voice channel.

        This also clears the queue.
        """

        channel = ctx.message.channel
        author  = ctx.message.author
        server  = ctx.message.guild

        # Check for role requirements
        requiredRole = self.settings.getServerStat(server, "RequiredStopRole")
        if requiredRole == "":
            #admin only
            isAdmin = ctx.message.author.permissions_in(ctx.message.channel).administrator
            if not isAdmin:
                checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
                for role in ctx.message.author.roles:
                    for aRole in checkAdmin:
                        # Get the role that corresponds to the id
                        if str(aRole['ID']) == str(role.id):
                            isAdmin = True
            if not isAdmin:
                await channel.send('You do not have sufficient privileges to access this command.')
                return
        else:
            #role requirement
            hasPerms = False
            for role in author.roles:
                if str(role.id) == str(requiredRole):
                    hasPerms = True
            if not hasPerms:
                await channel.send('You do not have sufficient privileges to access this command.')
                return

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return
        elif userInVoice == None:
            await ctx.channel.send('I\'m not in a voice channel.  Use the `{}summon`, `{}join [channel]` or `{}play [song]` commands to start playing something.'.format(ctx.prefix, ctx.prefix, ctx.prefix))
            return

        server = ctx.message.guild
        state = self.get_voice_state(server)

        self.settings.setServerStat(ctx.message.guild, "Volume", None)

        # Reset our playlist-related vars
        self.settings.setServerStat(ctx.guild, "Playlisting", None)
        self.settings.setServerStat(ctx.guild, "PlaylistRequestor", None)

        if state.is_playing():
            player = state.voice
            player.stop()

        try:
            state.audio_player.cancel()
            del self.voice_states[server.id]
            state.playlist = []
            state.repeat = False
            await state.voice.disconnect()
        except:
            pass

    @commands.command(pass_context=True, no_pm=True)
    async def skip(self, ctx):
        """Vote to skip a song. The song requester can automatically skip."""

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return
        elif userInVoice == None:
            await ctx.channel.send('I\'m not in a voice channel.  Use the `{}summon`, `{}join [channel]` or `{}play [song]` commands to start playing something.'.format(ctx.prefix, ctx.prefix, ctx.prefix))
            return

        state = self.get_voice_state(ctx.message.guild)
        if not state.voice.is_playing():
            await ctx.channel.send('Not playing anything right now...')
            return

        # Get song requester
        state = self.get_voice_state(ctx.message.guild)
        requester = state.playlist[0]['requester']
        requesterAdmin = requester.permissions_in(ctx.message.channel).administrator
        if not requesterAdmin:
            checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
            for role in requester.roles:
                for aRole in checkAdmin:
                    # Get the role that corresponds to the id
                    if str(aRole['ID']) == str(role.id):
                        requesterAdmin = True

        # Check if user is admin
        isAdmin = ctx.message.author.permissions_in(ctx.message.channel).administrator
        if not isAdmin:
            checkAdmin = self.settings.getServerStat(ctx.message.guild, "AdminArray")
            for role in ctx.message.author.roles:
                for aRole in checkAdmin:
                    # Get the role that corresponds to the id
                    if str(aRole['ID']) == str(role.id):
                        isAdmin = True
        if isAdmin:
            # Check if the requester is also an admin
            if not requesterAdmin:
                # Auto skip.
                await ctx.channel.send('My *Admin-Override* module is telling me to skip.')
                state.skip()
                return

        voter = ctx.message.author
        vote = await self.has_voted(ctx.message.author, state.votes)
        if vote != False:
            vote["value"] = 'skip'
        else:
            state.votes.append({ 'user': ctx.message.author, 'value': 'skip' })

        result = await self._vote_stats(ctx)

        if(result["total_skips"] >= result["total_keeps"]):
            await ctx.channel.send('Looks like skips WINS! sorry guys, skipping the song...')
            state.skip()
        # if voter == state.current.requester:
        # 	await ctx.channel.send('Requester requested skipping...')
        # 	state.skip()
        # elif voter.id not in state.skip_votes:
        # 	state.skip_votes.add(voter.id)
        # 	total_votes = len(state.skip_votes)
        # 	if total_votes >= 3:
        # 		await ctx.channel.send('Skip vote passed, skipping the song...')
        # 		state.skip()
        # 	else:
        # 		await ctx.channel.send('Skip vote added, currently at [{}/3]'.format(total_votes))
        # else:
        # 	await ctx.channel.send('You have already voted to skip this.')

    # @commands.command(pass_context=True, no_pm=True)
    # async def keep(self, ctx):
    # 	"""Vote to keep a song. The song requester can automatically skip.
    # 	"""

    @commands.command(pass_context=True, no_pm=True)
    async def keep(self, ctx):
        """Vote to keep a song."""

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return
        elif userInVoice == None:
            await ctx.channel.send('I\'m not in a voice channel.  Use the `{}summon`, `{}join [channel]` or `{}play [song]` commands to start playing something.'.format(ctx.prefix, ctx.prefix, ctx.prefix))
            return

        state = self.get_voice_state(ctx.message.guild)
        if not state.is_playing():
            await ctx.channel.send('Not playing anything right now...')
            return

        voter = ctx.message.author
        vote = await self.has_voted(ctx.message.author, state.votes)
        if vote != False:
            vote["value"] = 'keep'
        else:
            state.votes.append({ 'user': ctx.message.author, 'value': 'keep' })

        await self._vote_stats(ctx)

    @commands.command(pass_context=True, no_pm=True)
    async def unvote(self, ctx):
        """Remove your song vote."""
        state = self.get_voice_state(ctx.message.guild)
        if not state.is_playing():
            await ctx.channel.send('Not playing anything right now...')
            return

        voter = ctx.message.author
        vote = await self.has_voted(ctx.message.author, state.votes)
        if vote != False:
            for voted in state.votes:
                if(ctx.message.author == voted["user"]):
                    # Found our vote - remove it
                    state.votes.remove(voted)
        else:
            await ctx.channel.send('Your non-existent vote has been removed.')

        result = await self._vote_stats(ctx)

        if(result["total_skips"] >= result["total_keeps"]):
            await ctx.channel.send('Looks like skips WINS! sorry guys, skipping the song...')
            state.skip()

    @commands.command(pass_context=True, no_pm=True)
    async def vote_stats(self, ctx):
        return await self._vote_stats(ctx)

    async def _vote_stats(self, ctx):
        state = self.get_voice_state(ctx.message.guild)
        total_skips = 0
        total_keeps = 0
        for vote in state.votes:
            XP = self.settings.getUserStat(vote["user"], ctx.message.guild, "XP")
            if vote["value"] == 'skip':
                total_skips = total_skips + XP
            else:
                total_keeps = total_keeps + XP

        await ctx.channel.send('**Total Votes**:\nKeeps Score: *{:,}*\nSkips Score : *{:,}*'.format(total_keeps, total_skips))

        return {'total_skips': total_skips, 'total_keeps': total_keeps}

    async def has_voted(self, user , votes):

        for vote in votes:
            if(user == vote["user"]):
                return vote

        return False

    @commands.command(pass_context=True, no_pm=True)
    async def playing(self, ctx):
        """Shows info about currently playing."""

        state = self.get_voice_state(ctx.message.guild)
        if state.voice == None or not state.voice.is_playing():
            await ctx.channel.send('Not playing anything.')
        else:
            diff_time = state.total_playing_time  + (datetime.datetime.now() - state.start_time)

            if state.is_paused:
                diff_time = state.total_playing_time

            seconds = diff_time.total_seconds()
            hours = seconds // 3600
            minutes = (seconds % 3600) // 60
            seconds = seconds % 60

            #percent = diff_time.total_seconds() / state.current.player.duration * 100
            dSeconds = state.playlist[0]["duration"]
            percent = diff_time.total_seconds() / dSeconds * 100

            await ctx.channel.send('Now playing - *{}* [at {:02d}h:{:02d}m:{:02d}s] - {}%'.format(state.playlist[0]["song"],round(hours), round(minutes), round(seconds), round(percent, 2)))

    @commands.command(pass_context=True, no_pm=True)
    async def playlist(self, ctx):
        """Shows current songs in the playlist."""
        state = self.get_voice_state(ctx.message.guild)
        if len(state.playlist) <= 0:
                        await ctx.channel.send('No songs in the playlist')
                        return
        # Get our length
        totalSongs = len(state.playlist)
        if totalSongs > 15:
            playlist_string  = '**__Current Playlist (showing 1-15 out of {}):__**\n\n'.format(totalSongs)
        else:
            playlist_string  = '**__Current Playlist (1-{}):__**\n\n'.format(totalSongs)
        #playlist_string += '```Markdown\n'
        count = 1
        total_seconds = 0
        for i in state.playlist:
            if count > 15:
                break

            seconds = i["duration"]
            total_seconds += seconds
            hours = seconds // 3600
            minutes = (seconds % 3600) // 60
            seconds = seconds % 60

            playlist_string += '{}. *{}* - [{:02d}h:{:02d}m:{:02d}s] - requested by *{}*\n'.format(count, str(i["song"]),round(hours), round(minutes), round(seconds), DisplayName.name(i['requester']))
            count = count + 1
        #playlist_string += '```'
        hours = total_seconds // 3600
        minutes = (total_seconds % 3600) // 60
        seconds = total_seconds % 60
        playlist_string  += '\n**Total Time: **[{:02d}h:{:02d}m:{:02d}s]'.format(round(hours), round(minutes), round(seconds))
        if state.repeat:
            playlist_string += '\nRepeat is **on**'

        await ctx.channel.send(playlist_string)

    @commands.command(pass_context=True, no_pm=True)
    async def removesong(self, ctx, idx : int = None):
        """Removes a song in the playlist by the index."""

        # Check user credentials
        userInVoice = await self._user_in_voice(ctx)
        if userInVoice == False:
            await ctx.channel.send('You\'ll have to join the same voice channel as me to use that.')
            return
        elif userInVoice == None:
            await ctx.channel.send('I\'m not in a voice channel.  Use the `{}summon`, `{}join [channel]` or `{}play [song]` commands to start playing something.'.format(ctx.prefix, ctx.prefix, ctx.prefix))
            return

        channel = ctx.message.channel
        author  = ctx.message.author
        server  = ctx.message.guild

        canRemove = False
        # Check for role requirements
        requiredRole = self.settings.getServerStat(server, "RequiredStopRole")
        if requiredRole == "":
            #admin only
            isAdmin = author.permissions_in(channel).administrator
            if isAdmin:
                canRemove = True
        else:
            #role requirement
            hasPerms = False
            for role in author.roles:
                if str(role.id) == str(requiredRole):
                    hasPerms = True
            if hasPerms:
                canRemove = True

        if idx == None:
            await ctx.channel.send('Umm... Okay.  I successfully removed *0* songs from the playlist.  That\'s what you wanted, right?')
            return

        if not type(idx) == int:
            await ctx.channel.send('Indexes need to be integers, yo.')
            return

        idx = idx - 1
        state = self.get_voice_state(ctx.message.guild)
        if idx < 0 or idx >= len(state.playlist):
            await ctx.channel.send('Invalid song index, please refer to `{}playlist` for the song index.'.format(ctx.prefix))
            return
        current = state.playlist[idx]
        if idx == 0:
            await ctx.channel.send('Cannot delete currently playing song, use `{}skip` instead'.format(ctx.prefix))
            return
        if not current['requester'].id == ctx.message.author.id:
            # Not the owner of the song - check if we *can* delete
            if not canRemove:
                await channel.send('You do not have sufficient privileges to remove *other* users\' songs.')
                return
        await ctx.channel.send('Deleted *{}* from playlist'.format(str(current["song"])))
        del state.playlist[idx]


budget = float(input())
n = int(input())
m = int(input())
p = int(input())
#  N видеокарти, M процесора и P на брой рам памет

n_price = n * 250
m_price = 0.35 * n_price * m
p_price = 0.1 * n_price * p

total_bill = n_price + m_price +p_price

if n > m:
    total_bill -= 0.15 * total_bill

if budget >= total_bill:
    money_over = budget - total_bill
    print(f'You have {money_over:.2f} leva left!')
else:
    money_short = total_bill - budget
    print(f'Not enough money! You need {money_short:.2f} leva more!')