Beispiel #1
0
    async def quote_random(self,
                           ctx: vbu.Context,
                           user: discord.Member = None):
        """
        Gets a random quote for a given user.
        """

        # Get quote from database
        user = user or ctx.author
        async with vbu.Database() as db:
            quote_rows = await db(
                """SELECT quote_id as quote_id, user_id, channel_id, message_id
                FROM user_quotes WHERE user_id=$1 AND guild_id=$2 ORDER BY RANDOM() LIMIT 1""",
                user.id,
                ctx.guild.id,
            )
        if not quote_rows:
            return await ctx.send(
                f"{user.mention} has no available quotes.",
                allowed_mentions=discord.AllowedMentions.none())

        # Get the message
        data = quote_rows[0]
        if data['channel_id'] is None:
            self.logger.info(f"Deleting legacy quote - {data['quote_id']}")
            async with vbu.Database() as db:
                await db("DELETE FROM user_quotes WHERE quote_id=$1",
                         data['quote_id'])
            return await ctx.reinvoke()
        channel = self.bot.get_channel(data['channel_id'])
        if channel is None:
            self.logger.info(
                f"Deleting quote from deleted channel - {data['quote_id']}")
            async with vbu.Database() as db:
                await db("DELETE FROM user_quotes WHERE quote_id=$1",
                         data['quote_id'])
            return await ctx.reinvoke()
        try:
            message = await channel.fetch_message(data['message_id'])
            assert message is not None
        except (AssertionError, discord.HTTPException):
            self.logger.info(
                f"Deleting quote from deleted message - {data['quote_id']}")
            async with vbu.Database() as db:
                await db("DELETE FROM user_quotes WHERE quote_id=$1",
                         data['quote_id'])
            return await ctx.reinvoke()

        # Output to user
        quote_embed = message.embeds[0]
        quote_author = self.bot.get_user(data['user_id'])
        if quote_author:
            quote_embed.set_author(name=quote_author.display_name,
                                   icon_url=quote_author.display_avatar.url)
        return await ctx.send(embed=quote_embed)
    async def on_member_join(self, member: discord.Member):
        """
        Pings a member nickname update on member join.
        """

        # Check if they are a bot
        if member.bot:
            return

        # See if they have a permanent nickname set
        async with vbu.Database() as db:
            data = await db(
                """SELECT nickname FROM permanent_nicknames WHERE guild_id=$1 AND user_id=$2""",
                member.guild.id, member.id)
        if data:
            try:
                await member.edit(nick=data[0]["nickname"],
                                  reason="Changed by Apple.Py automagically")
                self.logger.info(
                    f"Set permanent nickname of {member.id} in {member.guild.id} from member join"
                )
            except discord.Forbidden as e:
                self.logger.error(
                    f"Couldn't set permanent nickname of {member.id} in {member.guild.id} - {e}"
                )
            return

        # See if we want to fun their name
        if self.bot.guild_settings[
                member.guild.id]['automatic_nickname_update']:
            self.logger.info(
                f"Pinging nickname update for member join (G{member.guild.id}/U{member.id})"
            )
            await self.fix_user_nickname(member)
    async def reminder_list(self, ctx: vbu.Context):
        """
        Shows you your reminders.
        """

        # Get the guild ID
        try:
            guild_id = ctx.guild.id
        except AttributeError:
            guild_id = 0

        # Grab their remidners
        async with vbu.Database() as db:
            rows = await db(
                "SELECT * FROM reminders WHERE user_id=$1 and guild_id=$2",
                ctx.author.id, guild_id)

        # Format an output string
        reminders = ""
        for reminder in rows:
            expiry = discord.utils.format_dt(reminder['timestamp'])
            reminders += f"\n`{reminder['reminder_id']}` - {reminder['message'][:70]} ({expiry})"
        message = f"Your reminders: {reminders}"

        # Send to the user
        await ctx.send(message or "You have no reminders.",
                       allowed_mentions=discord.AllowedMentions.none())
Beispiel #4
0
    async def points_leaderboard_show(self, ctx: vbu.Context):
        """
        Show the points leaderboard without creating a new one.
        """

        # Make some assertions
        assert ctx.guild

        # See if they're running a subcommand
        if ctx.invoked_subcommand is not None:
            return

        # Get data
        async with vbu.Database() as db:
            rows = await db(
                "SELECT * FROM user_points WHERE guild_id=$1 AND points > 0 ORDER BY points DESC",
                ctx.guild.id)

        # Format it into an embed
        valid_users = []
        for i in rows:
            member = ctx.guild.get_member(i['user_id'])
            if member is None:
                continue
            valid_users.append(f"{member.mention}: {i['points']:,}")
            if len(valid_users) >= 30:
                break
        with vbu.Embed(use_random_colour=True) as embed:
            embed.description = '\n'.join(
                valid_users) or 'Nobody is on here :c'

        # Output
        await ctx.send(embed=embed)
Beispiel #5
0
    async def on_command(self, ctx: commands.Context):
        """
        Count every time a command is run uwu
        """

        command = ctx.command
        if command is None:
            return
        command_name = command.name

        async with vbu.Database() as db:
            current_count = await db(
                "SELECT count FROM command_counter WHERE command_name=$1",
                command_name,
            )

            # Make sure we get a current count
            if current_count:
                current_count = current_count[0]["count"]
            else:
                current_count = 0

            await db(
                "INSERT INTO command_counter (command_name, count) VALUES ($1, $2) ON CONFLICT (command_name) DO UPDATE SET count = $2",
                command_name,
                current_count + 1,
            )

        self.bot.logger.info(f"Logging command completion: {command.name}")
async def user_feed(self, ctx, tank_chosen):

    async with vbu.Database() as db:
        upgrades = await db(
            """SELECT feeding_upgrade, big_servings_upgrade FROM user_upgrades WHERE user_id = $1""",
            ctx.author.id,
        )
        fish_rows = await db(
            """SELECT * FROM user_fish_inventory WHERE user_id = $1 AND tank_fish = $2""",
            ctx.author.id, tank_chosen
        )
        tank_rows = await db(
            """SELECT * FROM user_tank_inventory WHERE user_id = $1""",
            ctx.author.id,
        )
        item_rows = await db(
            """SELECT * FROM user_item_inventory WHERE user_id = $1""",
            ctx.author.id,
        )

    # For each fish in the selected fish rows add them to a list
    if not fish_rows:
        return await ctx.send("No fish in that tank!")

    fish_in_tank = []
    for fish in fish_rows:
        feed_time = dt(year=2005, month=5, day=1)
        if fish["fish_feed_time"]:
            feed_time = fish["fish_feed_time"]
        if fish["fish_alive"] == True and not (fish_feed_timeout := feed_time + FISH_FEED_COOLDOWN) > dt.utcnow():
            fish_in_tank.append(fish["fish_name"])
Beispiel #7
0
    async def rolepicker_name_autocomplete(
            self,
            ctx: vbu.SlashContext,
            interaction: discord.Interaction):
        """
        Handle autocompletes for rolepicker names.
        """

        async with vbu.Database() as db:
            role_picker_rows = await db.call(
                """
                SELECT
                    name
                FROM
                    role_pickers
                WHERE
                    guild_id = $1
                AND
                    name LIKE '%' || $2 || '%'
                """,
                interaction.guild_id,
                interaction.options[0].options[0].value,
            )
        return await interaction.response.send_autocomplete([
            discord.ApplicationCommandOptionChoice(name=i['name'], value=i['name'])
            for i in role_picker_rows
        ])
Beispiel #8
0
    async def timezone_get(self, ctx: vbu.Context, target: typing.Union[discord.Member, str] = None):
        """
        Get the current time for a given user.
        """

        # Check if they are a bot
        target = target or ctx.author
        target_is_timezone = False
        if isinstance(target, str):
            target_is_timezone = True
            target = self.get_common_timezone(target)
        if isinstance(target, discord.Member) and target.bot:
            return await ctx.send("I don't think bots have timezones...")

        # See if they've set a timezone
        if not target_is_timezone:
            async with vbu.Database() as db:
                rows = await db("SELECT timezone_name, timezone_offset FROM user_settings WHERE user_id=$1", target.id)
            if not rows or (rows[0]['timezone_name'] is None and rows[0]['timezone_offset'] is None):
                return await ctx.send(f"{target.mention} hasn't set up their timezone information! They can set it by running `{ctx.clean_prefix}timezone set`.")

        # Grab their current time and output
        if target_is_timezone:
            try:
                formatted_time = (discord.utils.utcnow().astimezone(pytz.timezone(target))).strftime('%-I:%M %p')
            except pytz.UnknownTimeZoneError:
                return await ctx.send("That isn't a valid timezone.")
            return await ctx.send(f"The current time in **{target}** is estimated to be **{formatted_time}**.")
        elif rows:
            if rows[0]['timezone_name']:
                formatted_time = (discord.utils.utcnow().astimezone(pytz.timezone(rows[0]['timezone_name']))).strftime('%-I:%M %p')
            else:
                formatted_time = (discord.utils.utcnow() + timedelta(minutes=rows[0]['timezone_offset'])).strftime('%-I:%M %p')
            await ctx.send(f"The current time for {target.mention} is estimated to be **{formatted_time}**.", allowed_mentions=discord.AllowedMentions.none())
async def user_revive(self, ctx, tank):
    # Get database vars

    async with vbu.Database() as db:
        fish_rows = await db(
            """SELECT * FROM user_fish_inventory WHERE user_id = $1 AND fish_alive = FALSE AND tank_fish = $2""",
            ctx.author.id, tank
        )
        revival_count = await db(
            """SELECT revival FROM user_item_inventory WHERE user_id = $1""",
            ctx.author.id,
        )

    # Make a list of all their fish
    fish_in_tank = []
    for fish in fish_rows:
        fish_in_tank.append(fish["fish_name"])

    # Create a select menu with their fish being choices
    fish = await utils.create_select_menu(
        self.bot, ctx, fish_in_tank, "dead fish", "revive", True
    )

    if revival_count[0]['revival'] <= 0:
        return await ctx.send("You have no revivals!")

    # If the fish isn't in a tank, it has no death timer, but if it is it's set to three days
    death_timer = dt.utcnow() + timedelta(days=3)
    message = f"{fish} is now alive, and will die {discord.utils.format_dt(death_timer, style='R')}!"

    # Set the database values
    async with vbu.Database() as db:
        await db(
            """UPDATE user_fish_inventory SET fish_alive = True, death_time = $3 WHERE user_id = $1 AND fish_name = $2""",
            ctx.author.id,
            fish,
            death_timer,
        )
        await db(
            """UPDATE user_item_inventory SET revival = revival - 1 WHERE user_id = $1""",
            ctx.author.id,
        )

    # Send message
    await ctx.send(
        message, allowed_mentions=discord.AllowedMentions.none()
    )
    async def on_message(self, message: discord.Message):
        """
        Sends GitHub/Lab links if a message sent in the server matches the format `gh/user/repo`.
        """

        if message.author.bot:
            return
        if (await self.bot.get_context(message)).command is not None:
            return

        # Find matches in the message
        m = re.finditer(
            (
                r'(?:\s|^)(?P<ident>g[hl])/(?P<url>(?P<user>[a-zA-Z0-9_-]{1,255})/(?P<repo>[a-zA-Z0-9_-]{1,255}))'
                r'(?:[#!]?(?P<issue>\d+?))?(?:\s|$)'
            ),
            message.content,
        )
        n = re.finditer(
            r'(?:\s|^)(?P<ident>g[hl]) (?P<alias>\S{1,255})(?: [#!]?(?P<issue>\d+?))?(?:\s|$)',
            message.content,
        )

        # Dictionary of possible Git() links
        git_dict = {
            "gh": "hub",
            "gl": "lab",
        }

        # Add the url of each matched link to the final output
        sendable = ""
        for i in m:
            url = i.group("url")
            ident = i.group("ident")
            issue = i.group("issue")
            url = f"https://git{git_dict[ident]}.com/{url}"
            if issue:
                if ident == "gh":
                    url = f"{url}/issues/{issue}"
                elif ident == "gl":
                    url = f"{url}/-/issues/{issue}"
            sendable += f"<{url}>\n"
        if n:
            async with vbu.Database() as db:
                for i in n:
                    issue = i.group("issue")
                    rows = await db("SELECT * FROM github_repo_aliases WHERE alias=$1", i.group("alias"))
                    if rows:
                        url = f"https://{rows[0]['host'].lower()}.com/{rows[0]['owner']}/{rows[0]['repo']}"
                        if issue:
                            if rows[0]['host'] == "Github":
                                url = f"{url}/issues/{issue}"
                            elif rows[0]['host'] == "Gitlab":
                                url = f"{url}/-/issues/{issue}"
                        sendable += f"<{url}>\n"

        # Send the GitHub links if there's any output
        if sendable:
            await message.channel.send(sendable, allowed_mentions=discord.AllowedMentions.none())
 async def increase_repo_usage_counter(self, user: typing.Union[discord.User, discord.Member], repo: GitRepo):
     async with vbu.Database() as db:
         await db(
             """INSERT INTO github_repo_uses (user_id, owner, repo, host, uses)
             VALUES ($1, $2, $3, $4, 1) ON CONFLICT (user_id, owner, repo, host)
             DO UPDATE SET uses=github_repo_uses.uses+excluded.uses""",
             user.id, repo.owner, repo.repo, repo.host,
         )
Beispiel #12
0
async def enough_to_craft(crafted: str, user_id: int):
    for item, required in items_required[crafted][0].items():
        async with vbu.Database() as db:
            amount = await db(
                f"""SELECT {item} FROM user_item_inventory WHERE user_id = $1""",
                user_id)
        if amount[0][item] < required:
            return False
    return True
Beispiel #13
0
    async def rolepicker_add(
            self,
            ctx: vbu.SlashContext,
            name: str,
            role: discord.Role):
        """
        Add a new role to a role picker.
        """

        # Defer so we can fetch
        await ctx.interaction.response.defer(ephemeral=True)

        # Fetch some values from the API
        author: discord.Member
        author = await ctx.guild.fetch_member(ctx.author.id)  # type: ignore - author will definitely exist
        guild: discord.Guild = ctx.guild
        guild_roles = guild.roles

        # Make sure the role they gave is lower than their top
        author_top_role = [i for i in guild_roles if i.id == author.roles[-1].id][0]
        role = [i for i in guild_roles if i.id == role.id][0]
        if author_top_role < role:
            return await ctx.interaction.followup.send(
                "Your top role is below the one you're trying to manage.",
                ephemeral=True,
            )

        # Add that role to the database
        async with vbu.Database() as db:
            await db.call(
                """
                INSERT INTO
                    role_picker_role
                    (
                        guild_id,
                        name,
                        role_id
                    )
                VALUES
                    (
                        $1,  -- guild_id
                        $2,  -- name
                        $3  -- role_id
                    )
                ON CONFLICT (guild_id, name, role_id)
                DO NOTHING
                """,
                guild.id, name, role.id,
            )

        # Tell the user it's done
        await ctx.interaction.followup.send(
            "Added role to role picker!~",
            ephemeral=True,
        )
        self.bot.dispatch("role_picker_update", guild, name)
Beispiel #14
0
    async def quote_force(self, ctx: vbu.Context,
                          messages: commands.Greedy[discord.Message]):
        """
        Quotes a user's message to the guild's quote channel.
        """

        # Make sure no subcommand is passed
        if ctx.invoked_subcommand is not None:
            return
        response = await self.get_quote_messages(ctx,
                                                 messages,
                                                 allow_self_quote=True)

        # Make embed
        if response['success'] is False:
            return await ctx.send(response['message'])
        embed = response['message']
        user = response['user']
        timestamp = response['timestamp']

        # See if they have a quotes channel
        quote_channel_id = self.bot.guild_settings[ctx.guild.id].get(
            'quote_channel_id')
        quote_id = create_id()
        embed.set_footer(text=f"Quote ID {quote_id.upper()}")
        posted_message = None
        if quote_channel_id:
            channel = self.bot.get_channel(quote_channel_id)
            try:
                posted_message = await channel.send(embed=embed)
            except (discord.Forbidden, AttributeError):
                pass
        if quote_channel_id is None or posted_message is None:
            return await ctx.send(
                "I couldn't send your quote into the quote channel.")

        # And save it to the database
        async with vbu.Database() as db:
            await db(
                """INSERT INTO user_quotes (quote_id, guild_id, channel_id,
                message_id, user_id, timestamp, quoter_id)
                VALUES ($1, $2, $3, $4, $5, $6, $7)""",
                quote_id,
                ctx.guild.id,
                posted_message.channel.id,
                posted_message.id,
                user.id,
                timestamp.replace(tzinfo=None),
                ctx.author.id,
            )

        # Output to user
        await ctx.send(
            f"{ctx.author.mention}'s quote saved with ID `{quote_id.upper()}`",
            embed=embed)
Beispiel #15
0
    async def topic(self, ctx: vbu.Context):
        """
        The parent group for the topic commands.
        """

        async with vbu.Database() as db:
            rows = await db("SELECT * FROM topics ORDER BY RANDOM() LIMIT 1")
        if not rows:
            return await ctx.send(
                "There aren't any topics set up in the database for this bot :<"
            )
        return await ctx.send(rows[0]['topic'])
Beispiel #16
0
    async def fish_food_death_loop(self):

        # Get all the fish that are in a tank and if they have a time of death, check to see if its past that time, then kill them if need be
        async with vbu.Database() as db:
            fish_rows = await db(
                """SELECT * FROM user_fish_inventory WHERE tank_fish != ''""")
            for fish_row in fish_rows:
                if fish_row["death_time"]:
                    if dt.utcnow() > fish_row["death_time"] and fish_row[
                            'fish_alive'] is True:
                        await db(
                            """UPDATE user_fish_inventory SET fish_alive=FALSE WHERE fish_name = $1 AND user_id = $2""",
                            fish_row["fish_name"], fish_row["user_id"])
Beispiel #17
0
    async def commanddata(self, ctx: commands.Context):
        """
        Send out the list of commands and their current count
        """

        # Get info from database
        async with vbu.Database() as db:
            command_data = await db("SELECT * FROM command_counter")

        # Make sure we have data
        if not command_data:
            return await ctx.send("No command data was found in the database.")

        # Set up the command list
        sorted_commands_singlelist = []
        commands_list = {}  # List of strings "**command name**: command count"
        total_count = 0  # To count the total number of commands
        for command in command_data:
            count = command["count"]
            total_count += count
        for command in command_data:
            count = command["count"]
            commands_list[
                f"**{command['command_name']}**: {count} times `({(count / total_count) * 100:.2f}%)`\n"
            ] = count
            # commands_list.append({count: f"**{command['command_name']}**: {count} times `({(count / total_count) * 100}%)`\n"})

        sorted_commands = sorted(
            commands_list.items(), key=lambda x: x[1], reverse=True
        )
        for i in sorted_commands:
            sorted_commands_singlelist.append(i[0])
        # Paginate

        # Set up the paginator formatter
        def formatter(menu, items):
            # Create the embed
            commands_embed = vbu.Embed(title="Command Data (times run)")
            # Add the total count footer
            commands_embed.set_footer(text=f"Total: {total_count}")
            # Add the command list to the emebd
            commands_embed.description = "\n".join(items)

            # Return the embed
            return commands_embed

        # Begin paginating
        pagin = vbu.Paginator(
            sorted_commands_singlelist, formatter=formatter, per_page=10
        )
        await pagin.start(ctx)
    async def issue_create_autocomplete(self, ctx: commands.SlashContext, interaction: discord.Interaction):
        """
        Send the user's most frequently used repos.
        """

        if not interaction.user:
            return await interaction.response.send_autocomplete(None)
        async with vbu.Database() as db:
            rows = await db(
                """SELECT * FROM github_repo_uses WHERE user_id=$1 ORDER BY uses DESC""",
                interaction.user.id,
            )
        responses = [
            discord.ApplicationCommandOptionChoice(name=(repo := str(GitRepo(r['host'], r['owner'], r['repo']))), value=repo)
            for r in rows
        ]
    async def repoalias_remove(self, ctx: vbu.Context, alias: str):
        """
        Removes a Github repo alias from the database.
        """

        async with vbu.Database() as db:
            data = await db(
                "SELECT * FROM github_repo_aliases WHERE alias=LOWER($1) AND added_by=$2",
                alias,
                ctx.author.id,
            )
            if not data:
                return await ctx.send(
                    "You don't own that repo alias.",
                    allowed_mentions=discord.AllowedMentions.none(),
                )
            await db("DELETE FROM github_repo_aliases WHERE alias=LOWER($1)", alias)
        await ctx.send("Done.")
Beispiel #20
0
    async def rolepicker_delete(
            self,
            ctx: vbu.SlashContext,
            name: str):
        """
        Remove a role picker message.
        """

        # Defer so we can database call
        await ctx.interaction.response.defer(ephemeral=True)

        # Delete stored info
        async with vbu.Database() as db:
            role_picker_rows = await db.call(
                """
                DELETE FROM
                    role_pickers
                WHERE
                    guild_id = $1
                AND
                    name = $2
                RETURNING *
                """,
                ctx.guild.id, name,
            )

        # See if we can delete the message as well
        row = role_picker_rows[0]
        messageable = self.bot.get_partial_messageable(
            row['channel_id'],
            type=discord.ChannelType.text,
        )
        message = messageable.get_partial_message(row['message_id'])
        try:
            await message.delete()
        except:
            pass

        # And tell them about it
        await ctx.interaction.followup.send(
            "Deleted role picker.",
            ephemeral=True,
        )
Beispiel #21
0
    async def points_leaderboard_create(self, ctx: vbu.Context):
        """
        Create a points leaderboard.
        """

        # Make some assertions
        assert ctx.guild

        message = await ctx.send("Setting up leaderboard message...")
        async with vbu.Database() as db:
            await db(
                """INSERT INTO guild_settings (guild_id, leaderboard_message_url) VALUES ($1, $2)
                ON CONFLICT (guild_id) DO UPDATE SET leaderboard_message_url=excluded.leaderboard_message_url""",
                ctx.guild.id,
                message.jump_url,
            )
        self.bot.guild_settings[
            ctx.guild.id]['leaderboard_message_url'] = message.jump_url
        self.bot.dispatch("leaderboard_update", ctx.guild)
Beispiel #22
0
 async def user_cast_loop(self):
     self.cast_time = dt.utcnow()
     async with vbu.Database() as db:
         casts = await db("""SELECT * FROM user_balance""")
         for x in casts:
             if x["casts"] >= 50:
                 continue
             amount_of_crafted = await db(
                 """SELECT fishing_boots FROM user_item_inventory WHERE user_id = $1""",
                 x["user_id"])
             if amount_of_crafted:
                 boot_multiplier = amount_of_crafted[0]['fishing_boots']
             else:
                 boot_multiplier = 0
             amount = random.choices([1, 2], [(1 - (.04 * boot_multiplier)),
                                              (.04 * boot_multiplier)])[0]
             await db(
                 """UPDATE user_balance SET casts=casts+$2 WHERE user_id = $1""",
                 x["user_id"], amount)
Beispiel #23
0
    async def on_leaderboard_update(self, guild: discord.Guild):
        """
        Update the leaderboard message.
        """

        # See if we can get the leaderboard message
        class FakeContext:
            bot = self.bot

        leaderboard_message_url = self.bot.guild_settings[
            guild.id]['leaderboard_message_url']
        if not leaderboard_message_url:
            return
        try:
            message = await commands.MessageConverter().convert(
                FakeContext, leaderboard_message_url)
        except commands.BadArgument:
            return
        if message is None:
            return

        # Get data
        async with vbu.Database() as db:
            rows = await db(
                "SELECT * FROM user_points WHERE guild_id=$1 AND points > 0 ORDER BY points DESC",
                guild.id)

        # Format it into an embed
        valid_users = []
        for i in rows:
            member = guild.get_member(i['user_id'])
            if member is None:
                continue
            valid_users.append(f"{member.mention}: {i['points']:,}")
            if len(valid_users) >= 30:
                break
        with vbu.Embed(use_random_colour=True) as embed:
            embed.description = '\n'.join(
                valid_users) or 'Nobody is on here :c'

        # Output
        await message.edit(content=None, embed=embed)
Beispiel #24
0
    async def rolepicker_remove(
            self,
            ctx: vbu.SlashContext,
            name: str,
            role: discord.Role):
        """
        Remove a role from one of your role pickers.
        """

        # Defer so we can fetch
        await ctx.interaction.response.defer(ephemeral=True)

        # Remove that role from the database
        async with vbu.Database() as db:
            removed_role = await db.call(
                """
                DELETE FROM
                    role_picker_role
                WHERE
                    guild_id = $1
                AND
                    name = $2
                AND
                    role_id = $3
                RETURNING *
                """,
                ctx.guild.id, name, role.id,
            )

        # See if anything was removed
        if removed_role:
            await ctx.interaction.followup.send(
                "Removed role from role picker.",
                ephemeral=True,
            )
        else:
            await ctx.interaction.followup.send(
                "That role wasn't in the picker.",
                ephemeral=True,
            )
            return
        self.bot.dispatch("role_picker_update", ctx.guild, name)
    async def convert(cls, ctx: vbu.Context, value: str):
        """
        Convert a string into an (host, owner, repo) string tuple.
        """

        # Let's strip the thing
        value = value.rstrip('/')

        # See if it's in the form `gh/owner/repo`
        if value.startswith("gh/"):
            _, owner, repo = value.split('/')
            host = "Github"

        # See if it's a Github url
        elif "github.com" in value.lower():
            match = re.search(r"(?:https?://)?github\.com/(?P<user>[a-zA-Z0-9_\-.]+)/(?P<repo>[a-zA-Z0-9_\-.]+)", value)
            assert match
            owner, repo = match.group("user"), match.group("repo")
            host = "Github"

        # See if it's in the form `gl/owner/repo`
        elif value.startswith("gl/"):
            _, owner, repo = value.split('/')
            host = "Gitlab"

        # See if it's a Gitlab url
        elif "gitlab.com" in value.lower():
            match = re.search(r"(?:https?://)?gitlab\.com/(?P<user>[a-zA-Z0-9_\-.]+)/(?P<repo>[a-zA-Z0-9_\-.]+)", value)
            assert match
            owner, repo = match.group("user"), match.group("repo")
            host = "Gitlab"

        # See if it's a repo alias
        else:
            async with vbu.Database() as db:
                repo_rows = await db("SELECT * FROM github_repo_aliases WHERE alias=LOWER($1)", value)
            if not repo_rows:
                raise commands.BadArgument("I couldn't find that git repo.")
            owner, repo, host = repo_rows[0]['owner'], repo_rows[0]['repo'], repo_rows[0]['host']

        # Sick we're done
        return cls(host, owner, repo)
Beispiel #26
0
    async def quote_get(self, ctx: vbu.Context, identifier: str):
        """
        Gets a quote from the guild's quote channel.
        """

        # Get quote from database
        async with vbu.Database() as db:
            quote_rows = await db(
                """SELECT user_quotes.quote_id as quote_id, user_id, channel_id, message_id FROM user_quotes LEFT JOIN
                quote_aliases ON user_quotes.quote_id=quote_aliases.quote_id
                WHERE user_quotes.quote_id=$1 OR quote_aliases.alias=$1""",
                identifier.lower(),
            )
        if not quote_rows:
            return await ctx.send(
                f"There's no quote with the identifier `{identifier.upper()}`.",
                allowed_mentions=discord.AllowedMentions.none(),
            )

        # Get the message
        data = quote_rows[0]
        if data['channel_id'] is None:
            return await ctx.send(
                "There's no quote channel set for that quote.")
        channel = self.bot.get_channel(data['channel_id'])
        if channel is None:
            return await ctx.send("I wasn't able to get your quote channel.")
        try:
            message = await channel.fetch_message(data['message_id'])
            assert message is not None
        except (AssertionError, discord.HTTPException):
            return await ctx.send("I wasn't able to get your quote message.")

        # try to refresh the user name and icon of the embed by getting the user from the user ID in the DB
        quote_embed = message.embeds[0]
        quote_author = self.bot.get_user(data['user_id'])
        if quote_author:
            quote_embed.set_author(name=quote_author.display_name,
                                   icon_url=quote_author.display_avatar.url)

        # Output to user
        return await ctx.send(embed=quote_embed)
Beispiel #27
0
    async def quote_delete(self, ctx: vbu.Context, quote_id: str):
        """
        Deletes a quote from your server.
        """

        # quote_ids = [i.lower() for i in quote_ids]
        quote_ids = [quote_id.lower()]
        quote_channel_id = self.bot.guild_settings[ctx.guild.id].get(
            'quote_channel_id')
        if quote_channel_id:
            quote_channel = self.bot.get_channel(quote_channel_id)
            try:
                async for message in quote_channel.history(limit=150):
                    if not message.author.id == ctx.guild.me.id:
                        continue
                    if not message.embeds:
                        continue
                    embed = message.embeds[0]
                    if not embed.footer:
                        continue
                    footer_text = embed.footer.text
                    if not footer_text:
                        continue
                    if not footer_text.startswith("Quote ID"):
                        continue
                    message_quote_id = footer_text.split(' ')[2].lower()
                    if message_quote_id in quote_ids:
                        try:
                            await message.delete()
                        except discord.HTTPException:
                            pass
            except (discord.HTTPException, AttributeError) as e:
                await ctx.send(e)

        async with vbu.Database() as db:
            await db(
                "DELETE FROM user_quotes WHERE quote_id=ANY($1) AND guild_id=$2",
                quote_ids, ctx.guild.id)
        return await ctx.send("Deleted quote(s).")
    async def repoalias_add(self, ctx: vbu.Context, alias: str, repo: GitRepo):
        """
        Add a Github repo alias to the database.
        """

        async with vbu.Database() as db:
            try:
                await db(
                    """INSERT INTO github_repo_aliases (alias, owner, repo, host, added_by)
                    VALUES (LOWER($1), $2, $3, $4, $5)""",
                    alias, repo.owner, repo.repo, repo.host, ctx.author.id,
                )
            except asyncpg.UniqueViolationError:
                data = await db("SELECT * FROM github_repo_aliases WHERE alias=LOWER($1) AND added_by=$2", alias, ctx.author.id)
                if not data:
                    return await ctx.send(
                        f"The alias `{alias.lower()}` is already in use.",
                        allowed_mentions=discord.AllowedMentions.none(),
                    )
                await db("DELETE FROM github_repo_aliases WHERE alias=$1", alias)
                return await self.repoalias_add(ctx, alias, repo)
        await ctx.send("Done.")
Beispiel #29
0
    async def points_remove(self,
                            ctx: vbu.Context,
                            user: typing.Optional[discord.Member],
                            points: int = 1):
        """
        Remove points from a user.
        """

        # Make some assertions
        assert isinstance(ctx.author, discord.Member)
        assert ctx.guild

        # Alter data
        user = user or ctx.author
        async with vbu.Database() as db:
            await db(
                """INSERT INTO user_points VALUES ($1, $2, $3) ON CONFLICT (guild_id, user_id)
                DO UPDATE SET points=user_points.points+excluded.points""",
                ctx.guild.id, user.id, -points)

        # Send output
        await ctx.send(f"Removed {points} points from {user.mention}.",
                       allowed_mentions=discord.AllowedMentions.none())
        self.bot.dispatch("leaderboard_update", ctx.guild)
Beispiel #30
0
    async def timezone_set(self, ctx: vbu.Context, *, offset: str = None):
        """
        Sets and stores your UTC offset into the bot.
        """

        # Ask them the question
        if offset is None:
            ask_message = await ctx.send((
                f"Hey, {ctx.author.mention}, what timezone are you currently in? You can give its name (`EST`, `GMT`, etc) "
                "or you can give your continent and nearest large city (`Europe/Amsterdam`, `Australia/Sydney`, etc) - this is "
                "case sensitive."
            ))
            try:
                check = lambda m: m.author.id == ctx.author.id and m.channel.id == ctx.channel.id
                response_message = await self.bot.wait_for("message", check=check, timeout=30)
                offset = response_message.content
            except asyncio.TimeoutError:
                return await ask_message.delete()

        # See if it's one of the more common ones that I know don't actually exist
        offset = self.get_common_timezone(offset)

        # Try and parse the timezone name
        try:
            zone = pytz.timezone(offset)
        except pytz.UnknownTimeZoneError:
            return await ctx.send(f"I can't work out what timezone you're referring to - please run this command again to try later, or go to the website (`{ctx.clean_prefix}info`) and I can work it out automatically.")

        # Store it in the database
        async with vbu.Database() as db:
            await db(
                """INSERT INTO user_settings (user_id, timezone_name) VALUES ($1, $2) ON CONFLICT (user_id)
                DO UPDATE SET timezone_name=excluded.timezone_name""",
                ctx.author.id, zone.zone,
            )
        await ctx.send(f"I think your current time is **{discord.utils.utcnow().astimezone(zone).strftime('%-I:%M %p')}** - I've stored this in the database.")