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)
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
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)
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)
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)
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)
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!")
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"))
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!")
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)