Exemple #1
0
    async def christmas(self, ctx):
        """
        Syntax: `{pre}{command_name}`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Enqueue a special shuffled playlist of holiday music. This command will
        only succeed in December, so use it while you can!

        __**Arguments**__
        This command takes no arguments.

        __**Example Usage**__
        `{pre}{command_name}`
        """
        if localnow().strftime("%m") != "12":
            return await ctx.send("It's not even december.")

        player = self.get_player(ctx)

        if not player.is_connected:
            channel = await player.connect(ctx)
            if not channel:
                return
            member = Member.obtain(ctx.author.id)
            await player.set_volume(member.last_volume)

        admin = Member.obtain(conf.ownerID)
        plentries = admin.playlists["Christmas"]
        random.shuffle(plentries)
        await player.add_enqueue_job(ctx, self.wavelink, "Christmas Music",
                                     plentries)
Exemple #2
0
    async def connect(self, ctx):
        """
        Syntax: `{pre}{command_name}`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Connects the bot to the voice channel you're in.
        You have to be in a voice channel for it to work.

        __**Arguments**__
        This command takes no arguments.

        __**Example Usage**__
        `{pre}{command_name}`
        """
        try:
            player = self.get_player(ctx)
            await player.connect(ctx)
            member = Member.obtain(ctx.author.id)
            await player.set_volume(member.last_volume)
        except AlreadyConnectedToChannel:
            await ctx.send("I'm already connected to a voice channel.")
        except NoVoiceChannel:
            await ctx.send(
                "You must be connected to a voice channel to play music.")
Exemple #3
0
    async def on_track_start(self, node, payload):
        """
        Handle the initialization of the info message and the player message.
        Also start tasks to update the interface periodically.
        """
        try:
            player, track = (payload.player,
                             payload.player.queue.current_track)
            member = Member.obtain(track.requester.id)
            member.update_history(track)
            messages = await track.ctx.channel.history(limit=2).flatten()
            messages = list([message.id for message in messages])
        except (QueueIsEmpty, EndOfQueue):
            return

        try:
            if player.infomsg.id in messages and player.plyrmsg.id in messages:
                await player.infomsg.edit(
                    embed=player.queue.info_embed(self.bot))
                await player.plyrmsg.edit(embed=player.player_embed())
            else:
                await player.infomsg.delete()
                await player.plyrmsg.delete()
                raise AttributeError
        except (AttributeError, discord.errors.NotFound):
            player.infomsg = await track.ctx.send(
                embed=player.queue.info_embed(self.bot))
            player.plyrmsg = await track.ctx.send(embed=player.player_embed())
            for button in list(BUTTONS.values()):
                await player.plyrmsg.add_reaction(button)
        except QueueIsEmpty:
            return
        player.interface_task = self.bot.loop.create_task(
            self.interface_updater(track.ctx, track.id))
Exemple #4
0
async def api_eq_delete(request):
    """Handle AJAX request to delete an equalizer."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    member = Member.obtain(uid)
    data = await request.json()
    eq = member.getEq(data['name'])

    if not eq:
        return web.Response(
            text=f"Error: No equalizer named '{data['name']}'.")

    if eq.name == member.current_eq:
        if len(member.equalizers) == 1:
            return web.Response(
                text="Error: You cannot delete your last equalizer.")
        else:
            member.current_eq = member.equalizers[0].name

    member.equalizers = list(
        [e for e in member.equalizers if e.name != eq.name])
    await updateEq(request.app.bot, member)
    member.save()

    return web.Response(text="Equalizer successfully deleted.")
Exemple #5
0
    async def add_playlist(self, ctx, *, name):
        """
        Syntax: `{pre}{command_name} <name>`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Adds a new playlist with the specified name.

        __**Arguments**__
        `<name>` - The name of the playlist to be created.

        __**Example Usage**__
        `{pre}{command_name} Electronic Music`
        `{pre}{command_name} Lo-Fi`
        """
        member = Member.obtain(ctx.author.id)
        try:
            member.add_playlist(name)
            await ctx.send("Playlist `" + name + "` has been added.")
        except PlaylistExists:
            await ctx.send("The specified playlist `" + name +
                           "` already exists.")
Exemple #6
0
    async def delete_playlist(self, ctx, *, name):
        """
        Syntax: `{pre}{command_name} <name>`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Deletes the new playlist with the specified name.

        __**Arguments**__
        `<name>` - The name of the playlist to be deleted.

        __**Example Usage**__
        `{pre}{command_name} Electronic Music`
        `{pre}{command_name} Lo-Fi`
        """
        member = Member.obtain(ctx.author.id)
        if not member.playlist_exists(name):
            return await ctx.send(f"No playlist named `{name}` exists.")
        if len(member.playlists[name]) == 0:
            member.del_playlist(name)
        else:
            warning = f"You are about to delete the playlist `{name}`, which still has songs in it. This action is __NOT REVERSABLE__."
            async with SimpleValidation(ctx, warning) as validation:
                if not validation:
                    return await ctx.send("Operation cancelled.")
                else:
                    member.del_playlist(name)
        return await ctx.send(f"The playlist `{name}` has been deleted.")
Exemple #7
0
    async def delete_song(self, ctx, *, title):
        """
        Syntax: `{pre}{command_name} <name>`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Removes the specified song from __ALL__ playlists. The song entered
        must __EXACTLY MATCH__ the title listed in the playlists. This command is
        distinguished from `{pre}delete_playlist_song` by the fact that it deletes
        the specified song from *ALL* playlists. **As a result, caution should be
        excersized when using this command.**

        __**Arguments**__
        `<name>` - The name of the song to be deleted.

        __**Example Usage**__
        `{pre}{command_name} Boney M. - Rasputin`
        """
        member = Member.obtain(ctx.author.id)
        try:
            if len(member.member_playlists(title)) > 1:
                warning = "This command deletes songs from __ALL__ playlists, and this song is apart of more than one playlist. This action is also not reversable."
                async with SimpleValidation(ctx, warning) as validation:
                    if not validation:
                        return await ctx.send("Operation cancelled.")
            entries = member.delete_from_all(title)
            return await ctx.send(
                f"Deleted `{title}` from {len(entries)} playlist(s).")
        except EntryNotFound:
            await ctx.send(
                f"No entry with a generator or title of `{title}` was found.")
Exemple #8
0
    async def show_playlist(self, ctx, *, name=None):
        """
        Syntax: `{pre}{command_name} [name]`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Lists the specified playlist. If no playlist is specified, it lists all
        playlists. A playlist with `🠊` in front of its name is the selected
        playlist.

        __**Arguments**__
        `[name]` - The name of the playlist to be displayed. If not specified,
        all playlists are shown.

        __**Example Usage**__
        `{pre}{command_name}`
        `{pre}{command_name} Lo-Fi`
        """
        member = Member.obtain(ctx.author.id)
        try:
            return await ctx.send(embed=member.playlist_embed(name=name))
        except PlaylistNotFound:
            return await ctx.send(
                f"The specified playlist `{name}` does not exist.")
Exemple #9
0
    async def delete_playlist_song(self, ctx, *, title):
        """
        Syntax: `{pre}{command_name} <name>`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Removes the specified song from the __SELECTED__ playlist. The song entered
        must __EXACTLY MATCH__ the title listed in the playlist. This command is
        distinguished from `{pre}delete_song` by the fact that it only deletes
        from the *selected* playlist, and not all playlists.

        __**Arguments**__
        `<name>` - The name of the song to be deleted.

        __**Example Usage**__
        `{pre}{command_name} Boney M. - Rasputin`
        """
        member = Member.obtain(ctx.author.id)
        if not member.selected:
            return await ctx.send("You don't have a playlist selected.")
        try:
            member.delete_playlist_entry(member.selected, title)
            return await ctx.send(
                f"Removed `{title}` from `{member.selected}`.")
        except EntryNotFound:
            await ctx.send(f"No entry identified by `{title}` exists.")
Exemple #10
0
    async def select_playlist(self, ctx, *, name):
        """
        Syntax: `{pre}{command_name} <playlist>`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Selects the specified playlist. This is used for operations which require
        a selected playlist. It will cause a playlist to appear with a `#` in front
        of it when `{pre}show_playlist` is run.

        __**Arguments**__
        `<playlist>` - The name of the song to be deleted.

        __**Example Usage**__
        `{pre}{command_name} Lo-Fi`
        """
        member = Member.obtain(ctx.author.id)
        if not member.playlist_exists(name):
            return await ctx.send(
                f"The specified playlist `{name}` does not exist.")
        name, _ = member.get_playlist(name)
        member.selected = name
        member.save()
        await ctx.send(f"`{name}` is now the selected playlist.")
Exemple #11
0
    async def history(self, ctx, member: discord.Member = None):
        """
        Syntax: `{pre}{command_name} [@member]`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Displays the history of songs that have been played by the specified member.
        If no member is specified, it defaults to the command executor.

        __**Arguments**__
        `[@member]` - The member whose history you want to see. If no member is
        specified, it defaults to the command executor.

        __**Example Usage**__
        `{pre}{command_name}`
        `{pre}{command_name} @Taira`
        """
        if not member:
            member = ctx.author
        output = f"__:musical_note: {member.name}'s Last 15 Songs :musical_note:__\n```css\n"
        member = Member.obtain(member.id)
        for i, entry in enumerate(reversed(member.history)):
            output += f"{i+1}. {entry}\n"
            if i + 1 == 15:
                output += '```'
                break
        return await ctx.send(output)
Exemple #12
0
async def api_eq_levelset(request):
    """Handle AJAX request to set the levels of an equalizer."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    member = Member.obtain(uid)
    data = await request.json()
    eq = member.getEq(data['name'])

    if not eq:
        return web.json_response(
            {'response': f"Error: No equalizer named '{data['name']}'."})

    eq.levels[data['band']] = data['level']
    eqs = []
    for e in member.equalizers:
        if e.name == eq.name:
            eqs.append(eq)
        else:
            eqs.append(e)
    member.equalizers = eqs
    await updateEq(request.app.bot, member)
    member.save()

    return web.json_response({'response': 'success'})
Exemple #13
0
    async def check_permissions(self, ctx, member: discord.Member = None):
        """
        Syntax: `{pre}{command_name} [@Member]`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Display the permissions list of the specified member. If no member is
        specified, it defaults to the author.

        __**Arguments**__
        `[@Member]` - The member whose permissions you want to see. If no
        member is specified, it defaults to the author.

        __**Example Usage**__
        `{pre}{command_name} @Taira`
        """
        if member is None:
            member = ctx.author
        memberobj = Member.obtain(member.id)
        if not memberobj.acl:
            return await ctx.send(
                "{user} has nothing in their ACL.".format(user=member.name))
        out = "__{user}'s ACL__\n```".format(user=member.name)
        for perm_node, value in memberobj.acl.items():
            out += "'{node}': {value}\n".format(node=perm_node, value=value)
        return await ctx.send(out + "```")
Exemple #14
0
async def settings_page(request):
    """Handle settings page."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']
    cleanGetParams(request)

    member = Member.obtain(uid)

    return {'bot': request.app.bot, 'member': member}
Exemple #15
0
async def playlists(request):
    """Handle playlist page."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    cleanGetParams(request)

    member = Member.obtain(uid)
    return {'playlists': member.playlists}
Exemple #16
0
    async def volume(self, ctx, volume):
        """
        Syntax: `{pre}{command_name}`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Sets the volume.

        The volume setting can either be a whole integer between 0 and 1000, or
        a whole number preprended with `+` or `-`. When a number is specified, the
        volume will simply be set to that number. If prepended with a `+` or `-`
        however, the number specified after will be added or subtracted from the
        current volume. For example, if the volume is `100` and `{pre}volume -10`
        is run, the volume will have `10` subtracted from it. `100 - 10 = 90`, and
        so the new volume would be `90`.

        __**Arguments**__
        `<volume>` - The volume setting. Must be either a whole number between 0
        and 1000, or a whole number preprended with `+` or `-`.

        __**Example Usage**__
        `{pre}{command_name} 10`
        `{pre}{command_name} +20`
        `{pre}{command_name} -5`
        """
        player = self.get_player(ctx)
        if not volume.isdigit():
            vol = volume.replace(" ", "")
            if vol.startswith("+") and vol.replace("+", "").isdigit():
                volume = player.volume + int(vol.replace("+", ""))
            elif vol.startswith("-") and vol.replace("-", "").isdigit():
                volume = player.volume - int(vol.replace("-", ""))
            else:
                return await ctx.send(
                    f"Invalid volume setting `{volume}`. Volume must be a whole number between 0 and 1000."
                )
        else:
            volume = int(volume)

        if volume > 1000 or volume < 0:
            return await ctx.send(
                f"Volume setting of `{volume}` outside of range. Volume must be between 0 and 1000."
            )

        member = Member.obtain(ctx.author.id)
        member.last_volume = volume
        member.save()

        await player.set_volume(volume)
        return await ctx.send(f"Volume set to {volume}%")
Exemple #17
0
    async def enqueue(self, ctx, *, playlist_rq):
        """
        Syntax: `{pre}{command_name} <playlist1>|[playlist2]|[playlist3] [--shuffle]`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Enqueues a playlist to be played. By default, it enqueues playlists
        in the order they exist in. Multiple playlists`--shuffle` can be specified to shuffle the
        playlist if desired.

        __**Arguments**__
        `<playlist>` - The name of the playlist to be enqueued. Only playlists
        you own can be enqueued by you. To see your playlists, you can run
        {pre}show_playlist. To define them, see the commands in the Playlisting
        cog.
        `[--shuffle]` - If specified, shuffles the playlist before enqueueing.

        __**Example Usage**__
        `{pre}{command_name} Electronic`
        `{pre}{command_name} Lo-Fi --shuffle`
        """
        playlist_rq, args = enqArgParse(playlist_rq)
        plentries = []
        member = Member.obtain(ctx.author.id)
        for playlist in playlist_rq.split("|"):
            playlist = member.playlist_exists(playlist.strip())
            if playlist is None:
                return await ctx.send(
                    f"No playlist named `{playlist_rq}` exists.")

            if len(member.playlists[playlist]) == 0:
                return await ctx.send(f"There are no entries in `{playlist}`.")
            plentries += member.playlists[playlist]

        if args['shuffle']:
            random.shuffle(plentries)

        player = self.get_player(ctx)

        if not player.is_connected:
            channel = await player.connect(ctx)
            if not channel:
                return
            await player.set_volume(member.last_volume)
        await player.add_enqueue_job(ctx,
                                     self.wavelink,
                                     playlist,
                                     plentries,
                                     mode=args['mode'])
Exemple #18
0
    async def handle_button(self, reaction, user):
        """Handle buttons."""
        if user.id == self.bot.user.id:
            return

        player = self.get_player(reaction.message.guild)
        try:
            if reaction.message.id != player.plyrmsg.id:
                return
        except AttributeError:
            if player.plyrmsg is None:
                return
            else:
                pass

        member = Member.obtain(user.id)

        if reaction.emoji == BUTTONS['LOWER_VOLUME']:
            volume = player.volume - member.settings.volumeStep
            await player.set_volume(volume)
        if reaction.emoji == BUTTONS['RAISE_VOLUME']:
            volume = player.volume + member.settings.volumeStep
            await player.set_volume(volume)
        if reaction.emoji == BUTTONS['STOP']:
            await player.halt()
            player.queue.clear()
            await player.plyrmsg.channel.send(
                f"Playback stopped by {user.name}.")
        if reaction.emoji == BUTTONS['DISCONNECT']:
            await player.teardown()
            await player.plyrmsg.channel.send(f"Disconnected by {user.name}.")
        if reaction.emoji == BUTTONS['PAUSE_RESUME']:
            await player.set_pause(not player.is_paused)
            state = "paused" if player.is_paused else "resumed"
            await player.plyrmsg.channel.send(f"Playback {state}.",
                                              delete_after=15)
        if reaction.emoji == BUTTONS['NEXT']:
            await player.stop()
        if reaction.emoji == BUTTONS['LAST']:
            player.queue.position -= 2
            await player.stop()
        if reaction.emoji == BUTTONS['LOOP_ONE']:
            player.queue.repeat_mode = Repeat.NONE if player.queue.repeat_mode != Repeat.NONE else Repeat.ONE
            mode = "REPEAT ONE" if player.queue.repeat_mode == Repeat.ONE else "OFF"
            await player.plyrmsg.channel.send(f"Repeat mode set to `{mode}`.",
                                              delete_after=15)
        if reaction.emoji == BUTTONS['LOOP_ALL']:
            mode = "REPEAT ALL" if player.queue.repeat_mode == Repeat.ALL else "OFF"
            await player.plyrmsg.channel.send(f"Repeat mode set to `{mode}`.",
                                              delete_after=15)
Exemple #19
0
async def api_deleteplaylist(request):
    """Handle AJAX request to delete playlist."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    member = Member.obtain(uid)
    data = await request.json()

    try:
        del member.playlists[data['id']]
    except KeyError:
        return web.Response(
            text=
            f"Error: Playlist '{data['id']}' not found in database. (Try reloading the page.)"
        )
    member.save()
    return web.Response(text="Playlist successfully deleted.")
Exemple #20
0
async def api_eq_change(request):
    """Handle AJAX request to change equalizers."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    member = Member.obtain(uid)
    data = await request.json()
    eq = member.getEq(data['name'])

    if not eq:
        return web.Response(
            text=f"Error: an equalizer named '{data['name']}' does not exist.")

    member.current_eq = eq.name
    await updateEq(request.app.bot, member)
    member.save()

    return web.Response(text="Equalizer successfully changed.")
Exemple #21
0
async def api_settings_save(request):
    """Handle AJAX request to save settings."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    member = Member.obtain(uid)
    data = await request.json()

    for setting, value in data.items():
        valid, range = member.settingInRange(setting, value)
        if not valid:
            return web.Response(
                text=
                f"Error: Invalid value for {setting}. Must be between {range[0]} and {range[1]}."
            )
        setattr(member.settings, setting, value)

    await updateEq(request.app.bot, member)
    member.save()
    return web.Response(text="Settings successfully saved.")
Exemple #22
0
async def api_eq_obtain(request):
    """Handle AJAX request to obtain equalizer."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    member = Member.obtain(uid)
    data = await request.json()
    eq = member.getEq(data['name'])

    if not eq:
        return web.json_response(
            {'response': f"Error: No equalizer named '{data['name']}'."})

    response = {
        'response': 'success',
        'name': eq.name,
        'description': eq.description,
        'levels': eq.levels
    }
    return web.json_response(response)
Exemple #23
0
async def api_eq_create(request):
    """Handle AJAX request to create an equalizer."""
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    member = Member.obtain(uid)
    data = await request.json()

    eq = member.getEq(data['name'])
    if eq:
        return web.Response(
            text=f"Error: An equalizer named '{data['name']}' already exists.")

    data['levels'] = member.getEq(
        data['based_on']
    ).levels if data['based_on'] is not None else Equalizer.DEFAULTS
    eq = Equalizer.buildFromJSON(data)

    member.equalizers.append(eq)
    member.save()
    return web.Response(text="New equalizer successfully made.")
Exemple #24
0
    def permissions_check(self, ctx):
        """Check permissions for the command."""
        member = Member.obtain(ctx.author.id)
        cog, command = self.node(ctx.bot).split(".")

        # Check for explicit deny
        for node, value in member.acl.items():
            if (node == cog + "." + command) or (node == cog + ".*"):
                if value == "deny":
                    return False

        if self.grant_level != "implicit":
            for node, value in member.acl.items():
                if (node == cog + "." + command) or (node == cog + ".*"):
                    if value == "allow":
                        return True
                elif node == "bot.*":
                    if value == "allow":
                        return True
                else:
                    pass
            return False
        else:
            return True
Exemple #25
0
    async def play(self, ctx, *, query):
        """
        Syntax: `{pre}{command_name} <song>`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Plays the requested song in the voice channel the bot is connected to.
        If {bot} is not connected, she will attempt to connect to the voice
        channel the command executor is in. If the command executor is not in a
        voice channel, this will fail.

        If a search is specified, {bot} will ask you which of the top five
        results you want. She will provide buttons to press, or you can type in
        the number corresponding to your selection if you want.

        __**Arguments**__
        `<song>` - A search term or URL that leads to the song you want played.
        This can manifest itself as a simple search, "Vince Guaraldi - Skating",
        or it can be entire links: "https://www.youtube.com/watch?v=dQw4w9WgXcQ".
        Links can be for Youtube, Soundcloud, and a myriad of other services.

        __**Example Usage**__
        `{pre}{command_name} Nasko - Break Through`
        `{pre}{command_name} https://www.youtube.com/watch?v=dQw4w9WgXcQ`
        """
        player = self.get_player(ctx)

        query, args = enqArgParse(query)

        if not player.is_connected:
            channel = await player.connect(ctx)
            if not channel:
                return
            member = Member.obtain(ctx.author.id)
            await player.set_volume(member.last_volume)

        member = Member.obtain(ctx.author.id)
        if not url_is_valid(query):
            query = f"ytsearch:{query}"
            tracks = await self.wavelink.get_tracks(query)
            if member.settings.promptOnSearch:
                track = await player.choose_track(ctx, tracks)
                if track is None:
                    return
            else:
                track = tracks[0]
        else:
            track = query
        try:
            await player.add_enqueue_job(ctx,
                                         self.wavelink,
                                         query.replace("ytsearch:", ""),
                                         [track],
                                         mode=args['mode'])
        except NoTracksFound:
            return await ctx.send(
                "I couldn't find a track with that name. Try being less specific, or use a link."
            )
Exemple #26
0
    async def add_song(self, ctx, *, cmdtext):
        """
        Syntax: `{pre}{command_name} <generator> [--options]`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Adds a song to the currently selected playlist with the specified options.
        The URL or search term specified should go to the song. (When `{pre}play`
        is run with the search term or URL, the desired song should play.)

        This command has a lot of options which can  be specified after the URL
        or search term that change certain things about how the song is stored:

        `--name <name>` - When specified, it overrides the default title of the
        video. This can be useful if the video title is long, or you want the name
        displayed in the playlist to be different.

        `--playlists <playlists>` - When specified, it overrides the default
        playlist that the song is added to. This is useful for when you want to
        add a song to multiple playlists at once. Playlists are specified by
        separating them with commas.

        `--start <starttime>` - When specified, the bot will start the video at
        the specified timestamp. If not specified, it defaults to the beginning
        of the video.

        `--end <endtime>` - When specified, the bot will end the video at the
        specified timestamp. If not specified, it defaults to the end of the
        video.

        __**Arguments**__
        `<generator>` - The URL or search term that spawns the song to be
        added. To know what URLs can be used, run `{pre}help play`.
        `[--options]` - Any of the options listed above. The order they are
        specified in, or whether or not all are specified does not matter.

        __**Example Usage**__
        `{pre}{command_name} South Park - Kyle's Mom's a Bitch`
        `{pre}{command_name} https://www.youtube.com/watch?v=aiSdTQ9DW9g --name Boney M. - Rasputin`
        `{pre}{command_name} https://www.youtube.com/watch?v=XFg43JqWmdM --name Pokemon Diamond and Pearl: Pokemon League (Night) Lo-Fi --playlists Lo-Fi, VGM --end: 4:32`
        """
        member = Member.obtain(ctx.author.id)
        generator, custom_title, playlists, start, end = parse_args(cmdtext)
        if not generator:
            return await ctx.send(
                "Song generator not specified. The generator is the URL or search term that spawns the video."
            )
        if not member.selected and not playlists:
            return await ctx.send(
                "You have not selected a playlist, nor have you specified a playlist to add this song to. You must do one or the other."
            )
        if not playlists:
            playlists = [member.selected]

        fakeplaylists = []
        for playlist in playlists:
            if not member.playlist_exists(playlist):
                fakeplaylists.append(playlist)
        if any(fakeplaylists):
            fakeplaylists = "`, `".join(fakeplaylists)
            return await ctx.send(
                f"The following playlists you specified do not exist:\n`{fakeplaylists}`"
            )
        try:
            entry = PlaylistEntry(generator=generator,
                                  custom_title=custom_title,
                                  start_time=start,
                                  end_time=end)
            for playlist in playlists:
                member.add_playlist_entry(playlist, entry)
            return await ctx.send("Entry added:", embed=entry.embed(member))
        except EntryExists:
            return await ctx.send(
                "The name or generator already exists in the selected or specified playlists."
            )
Exemple #27
0
async def api_saveplaylist(request):
    """
    Handle AJAX requests to save playlists.

    This handles both changes to playlists, and changes to the names
    of playlists.
    """
    session = await handleIdentity(request, scope="identify")
    uid = session['uid']

    member = Member.obtain(uid)
    data = await request.json()

    if data['name'].strip() == "":
        return web.Response(text="Error: You must specify a playlist name.")

    if data['action'] == "create" and member.playlist_exists(data['id']):
        return web.Response(
            text=f"Error: A playlist named \"{data['id']}\" already exists.")

    plentries = []
    repeats = []
    for i, entry in enumerate(data['entries']):
        if not entry[1]:
            return web.Response(
                text=f"Error on row {i+1}: The generator is a required field.")

        # Check for invalid timestamps.
        try:
            for j in (2, 3):
                if not entry[j]:
                    entry[j] = 0 if j == 2 else -1
                else:
                    entry[j] = serializeTimestamp(entry[j])
        except ValueError:
            field = "start time" if j == 2 else "end time"
            return web.Response(text=f"Error on row {i+1}: Invalid {field}.")

        # Check for repeats in title or generator.
        repeats_in_title = list([e[0]
                                 for e in data['entries']]).count(entry[0])
        repeats_in_gener = list([e[1]
                                 for e in data['entries']]).count(entry[1])
        if repeats_in_gener > 1 or (repeats_in_title > 1 and entry[0] != ""):
            repeats.append(str(i + 1))

        entry = PlaylistEntry(custom_title=entry[0],
                              generator=entry[1].strip(),
                              start=entry[2],
                              end=entry[3])
        plentries.append(entry)

    if len(repeats) != 0:
        resp = "Error: The following rows have repeats in the title or generator:"
        return web.Response(text=resp + ", ".join(repeats))

    if data['id'] == data['name'].strip():
        member.playlists[data['id']] = plentries
        member.save()
    else:
        del member.playlists[data['id']]
        member.playlists[data['name'].strip()] = plentries
        member.save()
        return web.Response(text="Playlist name updated!")

    return web.Response(text="Playlist saved successfully!")
Exemple #28
0
    async def set_permissions(self,
                              ctx,
                              member: discord.Member = None,
                              node=None,
                              value=None):
        """
        Syntax: `{pre}{command_name} <@Member> <node> <value>`

        **Aliases:** `{aliases}`
        **Node:** `{node}`
        **Grant Level:** `{grant_level}`

        __**Description**__
        Set permissions for the specified member at the specified node to the
        specified value.

        Permissions nodes for a given command are the name of the cog the
        command belongs to, followed by the non-alias name of the command itself
        in all lowercase letters. For example, this command belongs to the `Admin`
        cog, and is named `set_permissions`, so the node would be `admin.set_permissions`.

        {bot}'s permissions work on a deny-first basis. This means that if a
        member is denied permission for a command **anywhere** in their ACL, they
        will be unable to use that command, even if it is allowed elsewhere.

        By default, all commands are allowed to be run by anyone, except those
        commands whose grant level is set to `explicit`. Explicit grant level
        commands can only be run by those who have the node for the command
        explicitly set to `allow`. `remove`ing a node from a user's ACL will set
        the behavior of the command back to the default.

        Further, you can specify a wildcard by adding `.*` to the end of a cog.
        `admin.*` for example references all commands in the `Admin` cog. Finally,
        a special node exists, `bot.*` which references all commands, period.

        __**Arguments**__
        `<@Member>` - The member whose ACL you want to alter.
        `<node>` - The node in the member's ACL you want to set.
        `value` - The value to set the node to. Must be one of `allow`, `deny`,
        or `remove`.

        __**Example Usage**__
        `{pre}{command_name} @Taira bot.* allow`
        `{pre}{command_name} @Azura admin.restart deny`
        `{pre}{command_name} @Taira weather.* remove`
        """
        if member is None:
            return await ctx.send(
                "You must specify the user whose permissions you want to change."
            )
        if member.id == self.bot.user.id:
            return await ctx.send(
                "Access is denied. I'll manage my own permissions, thank you very much."
            )
        if node is None:
            return await ctx.send(
                "You must specify the node you want to modify.")
        if value is None:
            return await ctx.send(
                "You must specify what value to set the node to.")
        if value not in ['allow', 'deny', 'remove']:
            return await ctx.send(
                "Invalid value `{value}`. It must be `allow`, `deny`, or `remove`."
            )
        if not permission_exists(self.bot, node):
            return await ctx.send(
                "Invalid permissions node `{node}`. Permissions nodes follow the format `cog.command` or `cog.*` for all commands in a cog."
                .format(node=node))
        member_o = Member.obtain(member.id)
        if value == "remove":
            try:
                member_o.acl.pop(node)
                member_o.save()
                return await ctx.send(
                    "`{node}` removed from ACL for {user}.".format(
                        node=node, user=member.name))
            except KeyError:
                return await ctx.send(
                    "`{node}` not found in ACL for {user}.".format(
                        node=node, user=member.name))
        for n, val in member_o.acl.items():
            if n == node:
                if val == value:
                    return await ctx.send(
                        "`{node}` is already set to `{value}` for {user}.".
                        format(node=node, value=value, user=member.name))
        member_o.acl[node] = value
        member_o.save()
        return await ctx.send("`{node}` set to `{value}` for {user}.".format(
            node=node, value=value, user=member.name))