Update Shop cog to add refund functionality for role items, allowing users to return roles for inventory credits. Implement interactive confirmation process for refunds and enhance item gifting logic to restrict multiple role item ownership. Improve error handling and user feedback during transactions.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run
This commit is contained in:
parent
2f0baffab2
commit
12bd541b64
3 changed files with 253 additions and 26 deletions
|
@ -1,7 +1,6 @@
|
|||
{
|
||||
"author": [
|
||||
"Valerie",
|
||||
"Rose Haven Network"
|
||||
"Valerie"
|
||||
],
|
||||
"install_msg": "Thanks for adding Ruby Cogs! Join our Discord @ https://discord.gg/5CA8sewarU",
|
||||
"name": "Ruby Cogs",
|
||||
|
|
|
@ -1,12 +1,30 @@
|
|||
{
|
||||
"author" : ["Redjumpman (Redjumpman#1337)"],
|
||||
"install_msg" : "Thank you for installing shop. Be sure to check out the wiki here: https://github.com/Redjumpman/Jumper-Plugins/wiki/Shop-Red-V3\nThis cog comes with a bundled CSV file as an example for adding bulk shops and items.",
|
||||
"name" : "Shop",
|
||||
"short" : "Create, buy, trade, and redeem items.",
|
||||
"requirements" : ["tabulate"],
|
||||
"description" : "Shop system that allows for multiple shops with their own list of items for sale. Players can purchase these items with economy currency and redeem them for roles, or other server defined value.",
|
||||
"permissions" : ["Manage Messages", "Embed Links", "Add Reactions", "Manage Roles"],
|
||||
"tags" : ["Economy", "Fun", "Shop"],
|
||||
"min_python_version": [3, 6, 0],
|
||||
"author": [
|
||||
"Redjumpman (Redjumpman#1337)",
|
||||
"Valerie"
|
||||
],
|
||||
"install_msg": "Thank you for installing shop. Be sure to check out the wiki here: https://github.com/Redjumpman/Jumper-Plugins/wiki/Shop-Red-V3\nThis cog comes with a bundled CSV file as an example for adding bulk shops and items.",
|
||||
"name": "Shop",
|
||||
"short": "Create, buy, trade, and redeem items.",
|
||||
"requirements": [
|
||||
"tabulate"
|
||||
],
|
||||
"description": "Shop system that allows for multiple shops with their own list of items for sale. Players can purchase these items with economy currency and redeem them for roles, or other server defined value.",
|
||||
"permissions": [
|
||||
"Manage Messages",
|
||||
"Embed Links",
|
||||
"Add Reactions",
|
||||
"Manage Roles"
|
||||
],
|
||||
"tags": [
|
||||
"Economy",
|
||||
"Fun",
|
||||
"Shop"
|
||||
],
|
||||
"min_python_version": [
|
||||
3,
|
||||
6,
|
||||
0
|
||||
],
|
||||
"end_user_data_statement": "This cog stores discord IDs as needed for operation."
|
||||
}
|
||||
}
|
238
shop/shop.py
238
shop/shop.py
|
@ -396,6 +396,131 @@ class Shop(commands.Cog):
|
|||
await instance.Trading.set(not status)
|
||||
await ctx.send("Trading with you is now {}.".format("disabled" if status else "enabled"))
|
||||
|
||||
@shop.command()
|
||||
async def refund(self, ctx, *, item: str):
|
||||
"""Refunds a role item back to your inventory.
|
||||
|
||||
This command allows you to remove a role and get the shop item back in your inventory.
|
||||
You must specify the exact name of the role item from the shop.
|
||||
|
||||
Example:
|
||||
[p]shop refund VIP Role
|
||||
"""
|
||||
try:
|
||||
instance = await self.get_instance(ctx, settings=True)
|
||||
except AttributeError:
|
||||
return await ctx.send("You can't use this command in DMs when not in global mode.")
|
||||
|
||||
# Find the role item in any shop
|
||||
role_item = None
|
||||
role_name = None
|
||||
shop_name = None
|
||||
|
||||
async with instance.Shops() as shops:
|
||||
for shop in shops.values():
|
||||
for item_name, item_data in shop["Items"].items():
|
||||
if (item_name.lower() == item.lower() and
|
||||
item_data["Type"].lower() == "role"):
|
||||
role_item = item_name
|
||||
role_name = item_data["Role"]
|
||||
break
|
||||
if role_item:
|
||||
break
|
||||
|
||||
if not role_item:
|
||||
return await ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Item Not Found",
|
||||
description=f"Could not find a role item named '{item}' in any shop.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
|
||||
# Check if user has the role
|
||||
if role_name.startswith('<@&') and role_name.endswith('>'):
|
||||
role_id = int(role_name.strip('<@&>'))
|
||||
role = ctx.guild.get_role(role_id)
|
||||
else:
|
||||
role = discord.utils.get(ctx.guild.roles, name=role_name)
|
||||
|
||||
if not role:
|
||||
return await ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Role Not Found",
|
||||
description=f"The role associated with this item no longer exists on the server.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
|
||||
if role not in ctx.author.roles:
|
||||
return await ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Role Not Owned",
|
||||
description=f"You don't have the `{role.name}` role to refund.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
|
||||
# Ask for confirmation
|
||||
embed = discord.Embed(
|
||||
title="Confirm Role Refund",
|
||||
description=f"Are you sure you want to remove the `{role.name}` role and get `{role_item}` back in your inventory?",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
msg = await ctx.send(embed=embed)
|
||||
|
||||
# Add reactions for yes/no
|
||||
await msg.add_reaction("✅")
|
||||
await msg.add_reaction("❌")
|
||||
|
||||
def check(reaction, user):
|
||||
return user == ctx.author and str(reaction.emoji) in ["✅", "❌"]
|
||||
|
||||
try:
|
||||
reaction, user = await ctx.bot.wait_for("reaction_add", timeout=30.0, check=check)
|
||||
if str(reaction.emoji) == "✅":
|
||||
try:
|
||||
await ctx.author.remove_roles(role, reason="Shop role refund")
|
||||
# Add the role item back to inventory
|
||||
async with instance.Inventory() as inv:
|
||||
if role_item in inv:
|
||||
inv[role_item]["Qty"] += 1
|
||||
else:
|
||||
inv[role_item] = {
|
||||
"Qty": 1,
|
||||
"Type": "role",
|
||||
"Info": f"Grants the {role.name} role",
|
||||
"Role": role_name
|
||||
}
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Role Refunded",
|
||||
description=f"The `{role.name}` role has been removed and `{role_item}` has been added to your inventory.",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
await msg.edit(embed=embed)
|
||||
except discord.Forbidden:
|
||||
embed = discord.Embed(
|
||||
title="Refund Failed",
|
||||
description="I don't have permission to remove that role.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
await msg.edit(embed=embed)
|
||||
else:
|
||||
embed = discord.Embed(
|
||||
title="Refund Cancelled",
|
||||
description="Role refund cancelled.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
await msg.edit(embed=embed)
|
||||
except asyncio.TimeoutError:
|
||||
embed = discord.Embed(
|
||||
title="Timed Out",
|
||||
description="Role refund timed out.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
await msg.edit(embed=embed)
|
||||
|
||||
@shop.command()
|
||||
async def version(self, ctx):
|
||||
"""Shows the current Shop version."""
|
||||
|
@ -585,6 +710,7 @@ class Shop(commands.Cog):
|
|||
"""Gift another user a set number of one of your items.
|
||||
|
||||
The item must be in your inventory and have enough to cover the quantity.
|
||||
For role items, users can only have one copy at a time.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
@ -598,21 +724,61 @@ class Shop(commands.Cog):
|
|||
settings = await self.get_instance(ctx, settings=True)
|
||||
if not await settings.Settings.Gifting():
|
||||
return await ctx.send("Gifting is turned off.")
|
||||
|
||||
author_instance = await self.get_instance(ctx, user=ctx.author)
|
||||
author_inv = await author_instance.Inventory.all()
|
||||
if item not in author_inv:
|
||||
return await ctx.send(f"You don't own any `{item}`.")
|
||||
if author_inv[item]["Qty"] < quantity:
|
||||
return await ctx.send(f"You don't have that many `{item}` to give.")
|
||||
|
||||
# Check if it's a role item
|
||||
if author_inv[item]["Type"].lower() == "role":
|
||||
if quantity > 1:
|
||||
return await ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Invalid Quantity",
|
||||
description="You can only gift one copy of a role item.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
# Check if recipient already has the role item
|
||||
user_instance = await self.get_instance(ctx, user=user)
|
||||
user_inv = await user_instance.Inventory.all()
|
||||
if item in user_inv:
|
||||
return await ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Already Owned",
|
||||
description=f"{user.display_name} already owns this role item. Users can only have one copy at a time.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
|
||||
sm1 = ShopManager(ctx, instance=None, user_data=author_instance)
|
||||
await sm1.remove(item, number=quantity)
|
||||
|
||||
user_instance = await self.get_instance(ctx, user=user)
|
||||
sm2 = ShopManager(ctx, instance=None, user_data=user_instance)
|
||||
await sm2.add(item, author_inv[item], quantity)
|
||||
|
||||
await ctx.send(f"{ctx.author.mention} gifted {user.mention} {quantity}x {item}.")
|
||||
success = await sm2.add(item, author_inv[item], quantity)
|
||||
|
||||
if success:
|
||||
await ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Gift Successful",
|
||||
description=f"{ctx.author.mention} gifted {user.mention} {quantity}x {item}.",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
)
|
||||
else:
|
||||
# If gift failed, give item back to original owner
|
||||
await sm1.add(item, author_inv[item], quantity)
|
||||
await ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Gift Failed",
|
||||
description=f"Could not gift {item} to {user.mention}. The item has been returned to your inventory.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
|
||||
@shop.command()
|
||||
@global_permissions()
|
||||
|
@ -1293,6 +1459,27 @@ class ShopManager:
|
|||
)
|
||||
)
|
||||
|
||||
# Check if trying to buy multiple role items
|
||||
if item_data["Type"].lower() == "role":
|
||||
if quantity > 1:
|
||||
return await self.ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Invalid Quantity",
|
||||
description="You can only purchase one copy of a role item.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
# Check if user already has this role item
|
||||
async with self.user_data.Inventory() as inv:
|
||||
if item in inv:
|
||||
return await self.ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Already Owned",
|
||||
description="You already own this role item. You can only have one copy at a time.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
|
||||
# Validate quantity
|
||||
if quantity is None:
|
||||
return await self.ctx.send(
|
||||
|
@ -1384,15 +1571,29 @@ class ShopManager:
|
|||
|
||||
# Update stock and add to inventory
|
||||
await self.remove_stock(shop, item, stock, quantity)
|
||||
await self.add_to_inventory(item, item_data, quantity)
|
||||
success = await self.add_to_inventory(item, item_data, quantity)
|
||||
|
||||
await self.ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Purchase Successful",
|
||||
description=f"{self.ctx.author.mention} purchased {quantity}x {item} for {total_cost} {cur}.",
|
||||
color=discord.Color.green()
|
||||
if success:
|
||||
await self.ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Purchase Successful",
|
||||
description=f"{self.ctx.author.mention} purchased {quantity}x {item} for {total_cost} {cur}.",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Refund if inventory add failed (e.g. already had role item)
|
||||
try:
|
||||
await bank.deposit_credits(self.ctx.author, total_cost)
|
||||
except BalanceTooHigh as e:
|
||||
await bank.set_balance(self.ctx.author, e.max_balance)
|
||||
await self.ctx.send(
|
||||
embed=discord.Embed(
|
||||
title="Purchase Failed",
|
||||
description="You already own this item. Purchase has been refunded.",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
async def remove_stock(self, shop, item, current_stock, amount):
|
||||
"""Safely remove items from stock."""
|
||||
|
@ -1409,11 +1610,20 @@ class ShopManager:
|
|||
async def add_to_inventory(self, item, item_data, quantity):
|
||||
"""Safely add items to user inventory."""
|
||||
async with self.user_data.Inventory() as inv:
|
||||
if item in inv:
|
||||
inv[item]["Qty"] += quantity
|
||||
else:
|
||||
# For role items, only allow one copy
|
||||
if item_data["Type"].lower() == "role":
|
||||
if item in inv:
|
||||
return False # Already has the role item
|
||||
inv[item] = deepcopy(item_data)
|
||||
inv[item]["Qty"] = quantity
|
||||
inv[item]["Qty"] = 1 # Force quantity to 1 for role items
|
||||
return True
|
||||
else:
|
||||
if item in inv:
|
||||
inv[item]["Qty"] += quantity
|
||||
else:
|
||||
inv[item] = deepcopy(item_data)
|
||||
inv[item]["Qty"] = quantity
|
||||
return True
|
||||
|
||||
async def remove(self, item, number=1):
|
||||
"""Remove an item from user's inventory."""
|
||||
|
|
Loading…
Add table
Reference in a new issue