Esempio n. 1
0
    async def displayplant(self, ctx: utils.Context,
                           user: typing.Optional[utils.converters.UserID], *,
                           plant_name: str):
        """
        Shows you your plant status.
        """

        # Get data from database
        user = discord.Object(user) if user else ctx.author
        async with self.bot.database() as db:
            plant_rows = await db(
                "SELECT * FROM plant_levels WHERE user_id=$1 AND LOWER(plant_name)=LOWER($2)",
                user.id, plant_name)
            if not plant_rows:
                return await ctx.send(
                    f"You have no plant named **{plant_name.capitalize()}**",
                    allowed_mentions=discord.AllowedMentions(users=False,
                                                             roles=False,
                                                             everyone=False))

        # Filter into variables
        display_utils = self.bot.get_cog("PlantDisplayUtils")
        if plant_rows:
            display_data = display_utils.get_display_data(plant_rows[0],
                                                          user_id=user.id)
        else:
            display_data = display_utils.get_display_data(None,
                                                          user_id=user.id)

        # Generate text
        if display_data['plant_type'] is None:
            if ctx.author.id == user.id:
                text = f"You don't have a plant yet, {ctx.author.mention}! Run `{ctx.prefix}getplant` to get one!"
            else:
                text = f"<@{user.id}> doesn't have a plant yet!"
        else:
            text = f"<@{user.id}>'s {display_data['plant_type'].replace('_', ' ')} - **{plant_rows[0]['plant_name']}**"
            if int(display_data['plant_nourishment']) > 0:
                if ctx.author.id == user.id:
                    text += "!"
            elif int(display_data['plant_nourishment']) < 0:
                if ctx.author.id == user.id:
                    text += f". It's looking a tad... dead. Run `{ctx.prefix}deleteplant {plant_name}` to plant some new seeds."
                else:
                    text += ". It looks a bit... worse for wear, to say the least."
            elif int(display_data['plant_nourishment']) == 0:
                if ctx.author.id == user.id:
                    text += f". There are some seeds there, but you need to `{ctx.prefix}water {plant_rows[0]['plant_name']}` them to get them to grow."
                else:
                    text += ". There are some seeds there I think, but they need to be watered."

        # Send image
        image_data = display_utils.image_to_bytes(
            display_utils.get_plant_image(**display_data))
        file = discord.File(image_data, filename="plant.png")
        embed = utils.Embed(
            use_random_colour=True,
            description=text).set_image("attachment://plant.png")
        ctx._set_footer(embed)
        await ctx.send(embed=embed, file=file)
Esempio n. 2
0
    async def plants(self, ctx: utils.Context,
                     user: typing.Optional[discord.User]):
        """
        Shows you all the plants that a given user has.
        """

        # Grab the plant data
        user = user or ctx.author
        async with self.bot.database() as db:
            user_rows = await db(
                "SELECT * FROM plant_levels WHERE user_id=$1 ORDER BY plant_name DESC",
                user.id)

        # See if they have anything available
        plant_data = sorted([
            (i['plant_name'], i['plant_type'], i['plant_nourishment'],
             i['last_water_time'], i['plant_adoption_time']) for i in user_rows
        ])
        if not plant_data:
            embed = utils.Embed(use_random_colour=True,
                                description=f"<@{user.id}> has no plants :c")
            return await ctx.send(embed=embed)

        # Add the plant information
        embed = utils.Embed(use_random_colour=True,
                            description=f"<@{user.id}>'s plants")
        ctx._set_footer(embed)
        for plant_name, plant_type, plant_nourishment, last_water_time, plant_adoption_time in plant_data:
            plant_type_display = plant_type.replace('_', ' ').capitalize()
            plant_death_time = last_water_time + timedelta(
                **self.bot.config.get('plants', {}).get(
                    'death_timeout', {'days': 3}))
            plant_death_humanize_time = utils.TimeValue(
                (plant_death_time - dt.utcnow()).total_seconds()).clean_full
            plant_life_humanize_time = utils.TimeValue(
                (dt.utcnow() - plant_adoption_time).total_seconds()).clean_full
            if plant_nourishment == 0:
                text = f"{plant_type_display}, nourishment level {plant_nourishment}/{self.bot.plants[plant_type].max_nourishment_level}."
            elif plant_nourishment > 0:
                text = (
                    f"**{plant_type_display}**, nourishment level {plant_nourishment}/{self.bot.plants[plant_type].max_nourishment_level}.\n"
                    f"If not watered, this plant will die in **{plant_death_humanize_time}**.\n"
                    f"This plant has been alive for **{plant_life_humanize_time}**.\n"
                )
            else:
                text = f"{plant_type_display}, dead :c"
            embed.add_field(plant_name, text, inline=False)

        # Return to user
        v = await ctx.send(embed=embed)
        try:
            await self.bot.add_delete_button(v, (
                ctx.author,
                user,
            ),
                                             wait=False)
        except discord.HTTPException:
            pass
Esempio n. 3
0
    async def inventory(self,
                        ctx: utils.Context,
                        user: utils.converters.UserID = None):
        """
        Show you the inventory of a user.
        """

        # Get user info
        user = discord.Object(user) if user else ctx.author
        async with self.bot.database() as db:
            user_rows = await db(
                "SELECT * FROM user_settings WHERE user_id=$1", user.id)
            plant_rows = await db(
                "SELECT * FROM plant_levels WHERE user_id=$1", user.id)
            user_inventory_rows = await db(
                "SELECT * FROM user_inventory WHERE user_id=$1 AND amount > 0",
                user.id)

        # Start our embed
        embed = utils.Embed(use_random_colour=True, description="")
        ctx._set_footer(embed)

        # Format exp into a string
        if user_rows:
            exp_value = user_rows[0]['user_experience']
        else:
            exp_value = 0
        embed.description += f"<@{user.id}> has **{exp_value:,}** experience.\n"

        # Format plant limit into a string
        if user_rows:
            plant_limit = user_rows[0]['plant_limit']
        else:
            plant_limit = 1
        they_you = {True: "you", False: "they"}.get(user.id == ctx.author.id)
        their_your = {
            True: "your",
            False: "their"
        }.get(user.id == ctx.author.id)
        if plant_limit == len(plant_rows):
            embed.description += f"{they_you.capitalize()} are currently using all of {their_your} available {plant_limit} plant pots.\n"
        else:
            embed.description += f"{they_you.capitalize()} are currently using {len(plant_rows)} of {their_your} available {plant_limit} plant pots.\n"

        # Format inventory into a string
        if user_inventory_rows:
            inventory_string = "\n".join([
                f"{row['item_name'].replace('_', ' ').capitalize()} x{row['amount']:,}"
                for row in user_inventory_rows
            ])
            embed.add_field("Inventory", inventory_string)

        # Return to user
        return await ctx.send(embed=embed)
Esempio n. 4
0
    async def plants(self,
                     ctx: utils.Context,
                     user: utils.converters.UserID = None):
        """
        Shows you all the plants that a given user has.
        """

        # Grab the plant data
        user = discord.Object(user) if user else ctx.author
        async with self.bot.database() as db:
            user_rows = await db(
                "SELECT * FROM plant_levels WHERE user_id=$1 ORDER BY plant_name DESC",
                user.id)

        # See if they have anything available
        plant_data = sorted([(i['plant_name'], i['plant_type'],
                              i['plant_nourishment'], i['last_water_time'])
                             for i in user_rows])
        if not plant_data:
            embed = utils.Embed(use_random_colour=True,
                                description=f"<@{user.id}> has no plants :c")
            return await ctx.send(embed=embed)

        # Add the plant information
        embed = utils.Embed(use_random_colour=True,
                            description=f"<@{user.id}>'s plants")
        ctx._set_footer(embed)
        for plant_name, plant_type, plant_nourishment, last_water_time in plant_data:
            plant_type_display = plant_type.replace('_', ' ').capitalize()
            # plant_name_display = re.sub(r"([\_*`])", r"\\\1", plant_name)
            plant_death_time = last_water_time + timedelta(
                **self.bot.config.get('plants', {}).get(
                    'death_timeout', {'days': 3}))
            plant_death_humanize_time = arrow.get(plant_death_time).humanize(
                granularity=["day", "hour", "minute"], only_distance=True)
            if plant_nourishment == 0:
                text = f"{plant_type_display}, nourishment level {plant_nourishment}/{self.bot.plants[plant_type].max_nourishment_level}."
            elif plant_nourishment > 0:
                text = (
                    f"{plant_type_display}, nourishment level {plant_nourishment}/{self.bot.plants[plant_type].max_nourishment_level}.\n"
                    f"If not watered, this plant will die in *{plant_death_humanize_time}*."
                )
            else:
                text = f"{plant_type_display}, dead :c"
            embed.add_field(plant_name, text)

        # Return to user
        return await ctx.send(embed=embed)
Esempio n. 5
0
    async def waterplant(self, ctx: utils.Context, *, plant_name: str):
        """
        Increase the growth level of your plant.
        """

        # Let's run all the bullshit
        item = await self.water_plant_backend(ctx.author.id, plant_name)
        if item['success'] is False:
            return await ctx.send(item['text'])
        output_lines = item['text'].split("\n")

        # Try and embed the message
        embed = None
        if ctx.guild is None or ctx.channel.permissions_for(
                ctx.guild.me).embed_links:

            # Make initial embed
            embed = utils.Embed(use_random_colour=True,
                                description=output_lines[0])

            # Add multipliers
            if len(output_lines) > 1:
                embed.add_field("Multipliers",
                                "\n".join(
                                    [i.strip('') for i in output_lines[1:]]),
                                inline=False)

            # Add "please vote for Flower" footer
            counter = 0
            ctx._set_footer(embed)

            def check(footer_text) -> bool:
                if item['voted_on_topgg']:
                    return 'vote' not in footer_text
                return 'vote' in footer_text

            while counter < 100 and check(embed.footer.text.lower()):
                ctx._set_footer(embed)
                counter += 1

            # Clear the text we would otherwise output
            output_lines.clear()

        # Send message
        return await ctx.send("\n".join(output_lines), embed=embed)
Esempio n. 6
0
    async def displayall(self, ctx: utils.Context,
                         user: typing.Optional[discord.User]):
        """
        Show you all of your plants.
        """

        # Get data from database
        user = user or ctx.author
        async with self.bot.database() as db:
            plant_rows = await db(
                "SELECT * FROM plant_levels WHERE user_id=$1 ORDER BY plant_name DESC",
                user.id)
            if not plant_rows:
                return await ctx.send(f"<@{user.id}> has no available plants.",
                                      allowed_mentions=discord.AllowedMentions(
                                          users=[ctx.author]))
        await ctx.trigger_typing()

        # Filter into variables
        display_utils = self.bot.get_cog("PlantDisplayUtils")
        plant_rows = display_utils.sort_plant_rows(plant_rows)
        images = []
        for plant_row in plant_rows:
            if plant_row:
                display_data = display_utils.get_display_data(plant_row,
                                                              user_id=user.id)
            else:
                display_data = display_utils.get_display_data(None,
                                                              user_id=user.id)
            images.append(display_utils.get_plant_image(**display_data))

        # Get our images
        image = display_utils.compile_plant_images(*images)
        image_to_send = display_utils.image_to_bytes(image)
        text = f"Here are all of <@{user.id}>'s plants!"
        file = discord.File(image_to_send, filename="plant.png")
        embed = utils.Embed(
            use_random_colour=True,
            description=text).set_image("attachment://plant.png")
        ctx._set_footer(embed)
        await ctx.send(embed=embed, file=file)
Esempio n. 7
0
    async def volunteer(self, ctx: utils.Context):
        """
        Get the information for volunteering.
        """

        VOLUNTEER_INFORMATION = (
            "Want to help out with Flower, and watch it grow? Heck yeah! There's a few ways you can help out:\n\n"
            "**Art**\n"
            "Flower takes a lot of art, being a bot entirely about watching things grow. Unfortunately, I'm awful at art. Anything you can help out "
            "with would be amazing, if you had some kind of artistic talent yourself. If you [go here](https://github.com/Voxel-Fox-Ltd/Flower/blob/master/images/pots/clay/full.png) "
            "you can get an empty pot image you can use as a base. Every plant in Flower has a minimum of 6 distinct growth stages "
            "(which you can [see here](https://github.com/Voxel-Fox-Ltd/Flower/tree/master/images/plants/blue_daisy/alive) if you need an example).\n"
            "If this is the kind of thing you're interested in, I suggest you join [the support server](https://discord.gg/vfl) to ask for more information, or "
            "[email Kae](mailto://[email protected]) - the bot's developer.\n"
            "\n"
            "**Programming**\n"
            "If you're any good at programming, you can help out on [the bot's Github](https://github.com/Voxel-Fox-Ltd/Flower)! Ideas are discussed on "
            "[the support server](https://discord.gg/vfl) if you want to do that, but otherwise you can PR fixes, add issues, etc from there as you would "
            "with any other git repository.\n"
            "\n"
            "**Ideas**\n"
            "Flower is in constant need of feedback from the people who like to use it, and that's where you can shine. Even if you don't want to "
            "help out with art, programming, or anything else: it would be _absolutely amazing_ if you could give your experiences, gripes, and suggestions for "
            "Flower via the `{ctx.clean_prefix}suggest` command. That way I know where to change things, what to do to add new stuff, etcetc. If you want to "
            "discuss in more detail, I'm always around on [the support server](https://discord.gg/vfl)."
        ).format(ctx=ctx)
        embed = utils.Embed(
            use_random_colour=True,
            description=VOLUNTEER_INFORMATION,
        )
        ctx._set_footer(embed)
        try:
            await ctx.author.send(embed=embed)
        except discord.HTTPException:
            return await ctx.send("I wasn't able to send you a DM :<")
        if ctx.guild is not None:
            return await ctx.send("Sent you a DM!")
Esempio n. 8
0
    async def herbiary(self, ctx: utils.Context, *, plant_name: str = None):
        """
        Get the information for a given plant.
        """

        # See if a name was given
        if plant_name is None:
            plant_dict = collections.defaultdict(list)
            for plant in self.bot.plants.values():
                plant_dict[plant.plant_level].append(
                    plant.display_name.capitalize())
            embed_fields = []
            embed = utils.Embed(use_random_colour=True)
            for plant_level, plants in plant_dict.items():
                plants.sort()
                embed_fields.append(
                    (f"Level {plant_level}", "\n".join(plants)))
            for field in sorted(embed_fields):
                embed.add_field(*field, inline=True)
            ctx._set_footer(embed)
            return await ctx.send(embed=embed)

        # See if the given name is valid
        plant_name = plant_name.replace(' ', '_').lower()
        if plant_name not in self.bot.plants:
            return await ctx.send(
                f"There's no plant with the name **{plant_name.replace('_', ' ')}** :c",
                allowed_mentions=discord.AllowedMentions(users=False,
                                                         roles=False,
                                                         everyone=False))
        plant = self.bot.plants[plant_name]

        # Work out our artist info to be displayed
        description_list = []
        artist_info = self.artist_info.get(plant.artist, {}).copy()
        discord_id = artist_info.pop('discord', None)
        description_list.append(f"**Artist `{plant.artist}`**")
        if discord_id:
            description_list.append(
                f"Discord: <@{discord_id}> (`{discord_id}`)")
        for i, o in sorted(artist_info.items()):
            description_list.append(f"{i.capitalize()}: [Link]({o})")
        description_list.append("")

        # Work out some other info we want displayed
        description_list.append(f"Plant level {plant.plant_level}")
        description_list.append(f"Costs {plant.required_experience} exp")
        description_list.append(
            f"Gives between {plant.experience_gain['minimum']} and {plant.experience_gain['maximum']} exp"
        )

        # Embed the data
        with utils.Embed(use_random_colour=True) as embed:
            embed.title = plant.display_name.capitalize()
            embed.description = '\n'.join(description_list)
            embed.set_image("attachment://plant.png")
            ctx._set_footer(embed)
        display_utils = self.bot.get_cog("PlantDisplayUtils")
        plant_image_bytes = display_utils.image_to_bytes(
            display_utils.get_plant_image(plant.name, 21, "clay",
                                          random.randint(0, 360)))
        await ctx.send(embed=embed,
                       file=discord.File(plant_image_bytes,
                                         filename="plant.png"))
Esempio n. 9
0
    async def shop(self, ctx:utils.Context):
        """
        Shows you the available plants.
        """

        # Get data from the user
        async with self.bot.database() as db:
            user_rows = await db("SELECT * FROM user_settings WHERE user_id=$1", ctx.author.id)
            plant_level_rows = await db("SELECT * FROM plant_levels WHERE user_id=$1", ctx.author.id)
        if user_rows:
            user_experience = user_rows[0]['user_experience']
            plant_limit = user_rows[0]['plant_limit']
            last_plant_shop_time = user_rows[0]['last_plant_shop_time'] or dt(2000, 1, 1)
        else:
            user_experience = 0
            plant_limit = 1
            last_plant_shop_time = dt(2000, 1, 1)
        can_purchase_new_plants = dt.utcnow() > last_plant_shop_time + timedelta(**self.bot.config.get('plants', {}).get('water_cooldown', {'minutes': 15}))
        buy_plant_cooldown_delta = None
        if can_purchase_new_plants is False:
            buy_plant_cooldown_delta = utils.TimeValue(
                ((last_plant_shop_time + timedelta(**self.bot.config.get('plants', {}).get('water_cooldown', {'minutes': 15}))) - dt.utcnow()).total_seconds()
            )

        # Set up our initial items
        available_item_count = 0  # Used to make sure we can continue the command
        embed = utils.Embed(use_random_colour=True, description="")
        ctx._set_footer(embed)

        # See what we wanna get to doing
        embed.description += (
            f"What would you like to spend your experience to buy, {ctx.author.mention}? "
            f"You currently have **{user_experience:,} exp**, and you're using {len(plant_level_rows):,} of your {plant_limit:,} available plant pots.\n"
        )
        available_plants = await self.get_available_plants(ctx.author.id)

        # Add "can't purchase new plant" to the embed
        if can_purchase_new_plants is False:
            embed.description += f"\nYou can't purchase new plants for another **{buy_plant_cooldown_delta.clean}**.\n"

        # Add plants to the embed
        plant_text = []
        for plant in sorted(available_plants.values()):
            modifier = lambda x: x
            text = f"{plant.display_name.capitalize()} - `{plant.required_experience:,} exp`"
            if can_purchase_new_plants and plant.required_experience <= user_experience and len(plant_level_rows) < plant_limit:
                available_item_count += 1
            else:
                modifier = strikethrough
            plant_text.append(modifier(text))

        # Say when the plants will change
        now = dt.utcnow()
        remaining_time = utils.TimeValue((dt(now.year if now.month < 12 else now.year + 1, now.month + 1 if now.month < 12 else 1, 1) - now).total_seconds())
        plant_text.append(f"These plants will change in {remaining_time.clean_spaced}.")
        embed.add_field("Available Plants", '\n'.join(plant_text), inline=True)

        # Set up items to be added to the embed
        item_text = []

        # Add pots
        modifier = lambda x: x
        text = f"Pot - `{self.get_points_for_plant_pot(plant_limit):,} exp`"
        if user_experience >= self.get_points_for_plant_pot(plant_limit) and plant_limit < self.bot.config.get('plants', {}).get('hard_plant_cap', 10):
            available_item_count += 1
        else:
            modifier = strikethrough
        item_text.append(modifier(text))

        # Add variable items
        for item in self.bot.items.values():
            modifier = lambda x: x
            text = f"{item.display_name.capitalize()} - `{item.price:,} exp`"
            if user_experience >= item.price:
                available_item_count += 1
            else:
                modifier = strikethrough
            item_text.append(modifier(text))

        # Add all our items to the embed
        embed.add_field("Available Items", '\n'.join(item_text), inline=True)

        # Cancel if they don't have anything available
        if available_item_count == 0:
            embed.description += "\n**There is currently nothing available which you can purchase.**\n"
            return await ctx.send(embed=embed)
        else:
            embed.description += "\n**Say the name of the item you want to purchase, or type `cancel` to exit the shop with nothing.**\n"

        # Wait for them to respond
        shop_menu_message = await ctx.send(embed=embed)
        try:
            done, pending = await asyncio.wait([
                self.bot.wait_for("message", check=lambda m: m.author.id == ctx.author.id and m.channel == ctx.channel and m.content),
                self.bot.wait_for("raw_message_delete", check=lambda m: m.message_id == shop_menu_message.id),
            ], timeout=120, return_when=asyncio.FIRST_COMPLETED)
        except asyncio.TimeoutError:
            pass

        # See how they responded
        for future in pending:
            future.cancel()
        try:
            done = done.pop().result()
        except KeyError:
            return await ctx.send(f"Timed out asking for plant type {ctx.author.mention}.")
        if isinstance(done, discord.RawMessageDeleteEvent):
            return
        plant_type_message = done
        given_response = plant_type_message.content.lower().replace(' ', '_')

        # See if they want to cancel
        if given_response == "cancel":
            try:
                await plant_type_message.add_reaction("\N{OK HAND SIGN}")
            except discord.HTTPException:
                pass
            return

        # See if they want a plant pot
        if given_response == "pot":
            if plant_limit >= self.bot.config.get('plants', {}).get('hard_plant_cap', 10):
                return await ctx.send(f"You're already at the maximum amount of pots, {ctx.author.mention}! :c")
            if user_experience >= self.get_points_for_plant_pot(plant_limit):
                async with self.bot.database() as db:
                    await db(
                        """INSERT INTO user_settings (user_id, plant_limit, user_experience) VALUES ($1, 2, $2) ON CONFLICT (user_id) DO UPDATE
                        SET plant_limit=user_settings.plant_limit+1, user_experience=user_settings.user_experience-excluded.user_experience""",
                        ctx.author.id, self.get_points_for_plant_pot(plant_limit)
                    )
                return await ctx.send(f"Given you another plant pot, {ctx.author.mention}!")
            else:
                return await ctx.send(f"You don't have the required experience to get a new plant pot, {ctx.author.mention} :c")

        # See if they want a revival token
        item_type = self.bot.items.get(given_response)
        if item_type is not None:
            if user_experience >= item_type.price:
                async with self.bot.database() as db:
                    await db.start_transaction()
                    await db(
                        """INSERT INTO user_settings (user_id, user_experience) VALUES ($1, $2) ON CONFLICT (user_id) DO UPDATE
                        SET user_experience=user_settings.user_experience-excluded.user_experience""",
                        ctx.author.id, item_type.price
                    )
                    await db(
                        """INSERT INTO user_inventory (user_id, item_name, amount) VALUES ($1, $2, 1)
                        ON CONFLICT (user_id, item_name) DO UPDATE SET amount=user_inventory.amount+excluded.amount""",
                        ctx.author.id, item_type.name
                    )
                    await db.commit_transaction()
                return await ctx.send(f"Given you a **{item_type.display_name}**, {ctx.author.mention}!")
            else:
                return await ctx.send(f"You don't have the required experience to get a **{item_type.display_name}**, {ctx.author.mention} :c")

        # See if they want a plant
        try:
            plant_type = self.bot.plants[given_response]
        except KeyError:
            return await ctx.send(f"`{plant_type_message.content}` isn't an available plant name, {ctx.author.mention}!", allowed_mentions=discord.AllowedMentions(users=[ctx.author], roles=False, everyone=False))
        if can_purchase_new_plants is False:
            return await ctx.send(f"You can't purchase new plants for another **{buy_plant_cooldown_delta.clean}**.")
        if plant_type not in available_plants.values():
            return await ctx.send(f"**{plant_type.display_name.capitalize()}** isn't available in your shop this month, {ctx.author.mention} :c")
        if plant_type.required_experience > user_experience:
            return await ctx.send(f"You don't have the required experience to get a **{plant_type.display_name}**, {ctx.author.mention} (it requires {plant_type.required_experience}, you have {user_experience}) :c")
        if len(plant_level_rows) >= plant_limit:
            return await ctx.send(f"You don't have enough plant pots to be able to get a **{plant_type.display_name}**, {ctx.author.mention} :c")

        # Get a name for the plant
        await ctx.send("What name do you want to give your plant?")
        while True:
            try:
                plant_name_message = await self.bot.wait_for("message", check=lambda m: m.author.id == ctx.author.id and m.channel == ctx.channel and m.content, timeout=120)
            except asyncio.TimeoutError:
                return await ctx.send(f"Timed out asking for plant name {ctx.author.mention}.")
            plant_name = self.bot.get_cog("PlantCareCommands").validate_name(plant_name_message.content)
            if len(plant_name) > 50 or len(plant_name) == 0:
                await ctx.send("That name is too long! Please give another one instead!")
            else:
                break

        # Save the enw plant to database
        async with self.bot.database() as db:
            plant_name_exists = await db(
                "SELECT * FROM plant_levels WHERE user_id=$1 AND LOWER(plant_name)=LOWER($2)",
                ctx.author.id, plant_name
            )
            if plant_name_exists:
                return await ctx.send(f"You've already used the name `{plant_name}` for one of your other plants - please run this command again to give a new one!", allowed_mentions=discord.AllowedMentions(everyone=False, users=False, roles=False))
            await db(
                """INSERT INTO plant_levels (user_id, plant_name, plant_type, plant_nourishment, last_water_time, original_owner_id, plant_adoption_time, plant_pot_hue)
                VALUES ($1::BIGINT, $2, $3, 0, $4, $1::BIGINT, TIMEZONE('UTC', NOW()), CAST($1::BIGINT % 360 AS SMALLINT)) ON CONFLICT (user_id, plant_name) DO UPDATE
                SET plant_nourishment=0, last_water_time=$4""",
                ctx.author.id, plant_name, plant_type.name, dt(2000, 1, 1),
            )
            await db(
                "UPDATE user_settings SET user_experience=user_settings.user_experience-$2, last_plant_shop_time=TIMEZONE('UTC', NOW()) WHERE user_id=$1",
                ctx.author.id, plant_type.required_experience,
            )
        await ctx.send(f"Planted your **{plant_type.display_name}** seeds!")
Esempio n. 10
0
    async def waterplant(self, ctx: utils.Context, *, plant_name: str):
        """
        Increase the growth level of your plant.
        """

        # Decide on our plant type - will be ignored if there's already a plant
        db = await self.bot.database.get_connection()

        # See if they have a plant available
        plant_level_row = await db(
            "SELECT * FROM plant_levels WHERE user_id=$1 AND LOWER(plant_name)=LOWER($2)",
            ctx.author.id, plant_name)
        if not plant_level_row:
            await db.disconnect()
            return await ctx.send(
                f"You don't have a plant with the name **{plant_name}**! Run `{ctx.prefix}getplant` to plant some new seeds, or `{ctx.prefix}plants` to see the list of plants you have already!",
                allowed_mentions=discord.AllowedMentions(users=False,
                                                         roles=False,
                                                         everyone=False))
        plant_data = self.bot.plants[plant_level_row[0]['plant_type']]

        # See if they're allowed to water things
        if plant_level_row[0]['last_water_time'] + timedelta(
                **self.bot.config.get('plants', {}).get(
                    'water_cooldown', {'minutes': 15})) > dt.utcnow(
                    ) and ctx.author.id not in self.bot.owner_ids:
            await db.disconnect()
            timeout = utils.TimeValue(
                ((plant_level_row[0]['last_water_time'] +
                  timedelta(**self.bot.config.get('plants', {}).get(
                      'water_cooldown', {'minutes': 15}))) -
                 dt.utcnow()).total_seconds())
            return await ctx.send(
                f"You need to wait another {timeout.clean_spaced} to be able water your {plant_level_row[0]['plant_type'].replace('_', ' ')}."
            )
        last_water_time = plant_level_row[0]['last_water_time']

        # See if the plant should be dead
        if plant_level_row[0]['plant_nourishment'] < 0:
            plant_level_row = await db(
                """UPDATE plant_levels SET
                plant_nourishment=LEAST(-plant_levels.plant_nourishment, plant_levels.plant_nourishment), last_water_time=$3
                WHERE user_id=$1 AND LOWER(plant_name)=LOWER($2) RETURNING *""",
                ctx.author.id,
                plant_name,
                dt.utcnow(),
            )

        # Increase the nourishment otherwise
        else:
            plant_level_row = await db(
                """UPDATE plant_levels
                SET plant_nourishment=LEAST(plant_levels.plant_nourishment+1, $4), last_water_time=$3
                WHERE user_id=$1 AND LOWER(plant_name)=LOWER($2) RETURNING *""",
                ctx.author.id,
                plant_name,
                dt.utcnow(),
                plant_data.max_nourishment_level,
            )

        # Add to the user exp if the plant is alive
        user_plant_data = plant_level_row[0]
        gained_experience = 0
        original_gained_experience = 0
        multipliers = []  # List[Tuple[float, "reason"]]
        additional_text = []  # List[str]
        voted_on_topgg = False

        # And now let's water the damn thing
        if user_plant_data['plant_nourishment'] > 0:

            # Get the experience that they should have gained
            gained_experience = plant_data.get_experience()
            original_gained_experience = gained_experience

            # See if we want to give them a 30 second water-time bonus
            if dt.utcnow() - last_water_time - timedelta(
                    **self.bot.config.get('plants', {}).get(
                        'water_cooldown', {'minutes': 15})) <= timedelta(
                            seconds=30):
                multipliers.append((
                    1.5,
                    "You watered within 30 seconds of your plant's cooldown resetting."
                ))

            # See if we want to give the new owner bonus
            if plant_level_row[0]['user_id'] != plant_level_row[0][
                    'original_owner_id']:
                multipliers.append(
                    (1.05, "You watered a plant that you got from a trade."))

            # See if we want to give them the voter bonus
            if self.bot.config.get(
                    'bot_listing_api_keys',
                {}).get('topgg_token') and await self.get_user_voted(
                    ctx.author.id):
                multipliers.append((
                    1.1,
                    f"You [voted for the bot](https://top.gg/bot/{self.bot.user.id}/vote) on Top.gg."
                ))
                voted_on_topgg = True

            # See if we want to give them the plant longevity bonus
            if user_plant_data['plant_adoption_time'] < dt.utcnow(
            ) - timedelta(days=7):
                multipliers.append(
                    (1.2, "Your plant has been alive for longer than a week."))

            # Add the actual multiplier values
            for multiplier, _ in multipliers:
                gained_experience *= multiplier

            # Update db
            gained_experience = int(gained_experience)
            await db(
                """INSERT INTO user_settings (user_id, user_experience) VALUES ($1, $2) ON CONFLICT (user_id)
                DO UPDATE SET user_experience=user_settings.user_experience+$2""",
                ctx.author.id,
                gained_experience,
            )

        # Send an output
        await db.disconnect()
        if user_plant_data['plant_nourishment'] < 0:
            return await ctx.send(
                "You sadly pour water into the dry soil of your silently wilting plant :c"
            )

        # Set up our output text
        gained_exp_string = f"**{gained_experience}**" if gained_experience == original_gained_experience else f"~~{original_gained_experience}~~ **{gained_experience}**"
        output_lines = []
        if plant_data.get_nourishment_display_level(
                user_plant_data['plant_nourishment']
        ) > plant_data.get_nourishment_display_level(
                user_plant_data['plant_nourishment'] - 1):
            output_lines.append(
                f"You gently pour water into **{plant_level_row[0]['plant_name']}**'s soil, gaining you {gained_exp_string} experience, watching your plant grow!~"
            )
        else:
            output_lines.append(
                f"You gently pour water into **{plant_level_row[0]['plant_name']}**'s soil, gaining you {gained_exp_string} experience~"
            )
        for m, t in multipliers:
            output_lines.append(f"**{m}x**: {t}")
        for t in additional_text:
            output_lines.append(t)

        # Try and embed the message
        embed = None
        if ctx.guild is None or ctx.channel.permissions_for(
                ctx.guild.me).embed_links:

            # Make initial embed
            embed = utils.Embed(use_random_colour=True,
                                description=output_lines[0])

            # Add multipliers
            if len(output_lines) > 1:
                embed.add_field("Multipliers",
                                "\n".join(
                                    [i.strip('') for i in output_lines[1:]]),
                                inline=False)

            # Add "please vote for Flower" footer
            counter = 0
            ctx._set_footer(embed)
            check = lambda text: 'vote' in text if voted_on_topgg else False  # Return True to change again - force to "vote for flower" if they haven't voted, else anything but
            while counter < 100 and check(embed.footer.text.lower()):
                ctx._set_footer(embed)
                counter += 1

            # Clear the text we would otherwise output
            output_lines.clear()

        # Send message
        return await ctx.send("\n".join(output_lines), embed=embed)