diff --git a/inventory/__init__.py b/inventory/__init__.py new file mode 100644 index 0000000..9f952c6 --- /dev/null +++ b/inventory/__init__.py @@ -0,0 +1,8 @@ +from .inventory import Inventory + +__red_end_user_data_statement__ = "This cog stores user inventory data and trading history. Users can delete their data using the built-in data deletion commands." + +async def setup(bot): + cog = Inventory(bot) + await cog.initialize() + await bot.add_cog(cog) \ No newline at end of file diff --git a/inventory/info.json b/inventory/info.json new file mode 100644 index 0000000..0d041e2 --- /dev/null +++ b/inventory/info.json @@ -0,0 +1,17 @@ +{ + "author": [ + "Valerie" + ], + "install_msg": "Thanks for installing the Inventory cog! Use `[p]help Inventory` to see all commands.", + "name": "Inventory", + "short": "Manage inventories and trade items between users", + "description": "A cog that allows users to manage inventories and trade items with other users. Features include viewing inventories, trading items, and checking item information.", + "tags": [ + "inventory", + "trading", + "items", + "economy" + ], + "min_bot_version": "3.5.0", + "end_user_data_statement": "This cog stores user inventory data and trading history. Users can delete their data using the built-in data deletion commands." +} \ No newline at end of file diff --git a/inventory/inventory.py b/inventory/inventory.py new file mode 100644 index 0000000..c17fd3f --- /dev/null +++ b/inventory/inventory.py @@ -0,0 +1,245 @@ +import asyncio +import discord +from redbot.core import commands, Config # type: ignore +from redbot.core.bot import Red # type: ignore +from redbot.core.utils.chat_formatting import box, humanize_list # type: ignore +from typing import Dict, List, Optional + +class Inventory(commands.Cog): + """A cog for managing user inventories and trading items between users.""" + + def __init__(self, bot: Red): + self.bot = bot + self.config = Config.get_conf( + self, + identifier=582650110, # Unique identifier for this cog + force_registration=True + ) + + # Default user settings + default_user = { + "inventory": {}, # {item_name: quantity} + "trade_history": [], # List of recent trades + "trading_with": None, # User currently trading with + "trade_offer": {}, # Current trade offer + "trade_accepted": False # Whether user has accepted current trade + } + + # Default guild settings + default_guild = { + "items": { + "common_sword": {"name": "Common Sword", "value": 100, "description": "A basic sword"}, + "health_potion": {"name": "Health Potion", "value": 50, "description": "Restores health"}, + "magic_scroll": {"name": "Magic Scroll", "value": 200, "description": "Contains magical spells"} + } + } + + self.config.register_user(**default_user) + self.config.register_guild(**default_guild) + + async def initialize(self): + """Initialize the cog.""" + pass + + @commands.group() + async def inventory(self, ctx: commands.Context): + """Inventory management commands""" + if ctx.invoked_subcommand is None: + await self.show_inventory(ctx) + + async def show_inventory(self, ctx: commands.Context): + """Show a user's inventory""" + inventory = await self.config.user(ctx.author).inventory() + if not inventory: + await ctx.send("Your inventory is empty!") + return + + guild_items = await self.config.guild(ctx.guild).items() + + # Format inventory display + output = ["Your Inventory:"] + for item_id, quantity in inventory.items(): + if item_id in guild_items: + item = guild_items[item_id] + output.append(f"{item['name']} (x{quantity}) - Worth: {item['value']} coins") + + await ctx.send(box("\n".join(output))) + + @commands.command() + async def additem(self, ctx: commands.Context, item_id: str, quantity: int = 1): + """Add an item to your inventory (Admin only)""" + if not await self.bot.is_owner(ctx.author): + await ctx.send("Only the bot owner can add items!") + return + + guild_items = await self.config.guild(ctx.guild).items() + if item_id not in guild_items: + await ctx.send(f"Invalid item ID! Available items: {', '.join(guild_items.keys())}") + return + + async with self.config.user(ctx.author).inventory() as inventory: + if item_id in inventory: + inventory[item_id] += quantity + else: + inventory[item_id] = quantity + + await ctx.send(f"Added {quantity}x {guild_items[item_id]['name']} to your inventory!") + + @commands.group() + async def trade(self, ctx: commands.Context): + """Trading commands""" + pass + + @trade.command(name="offer") + async def trade_offer(self, ctx: commands.Context, user: discord.Member, item_id: str, quantity: int = 1): + """Offer to trade an item to another user""" + if user.bot: + await ctx.send("You can't trade with bots!") + return + + if user == ctx.author: + await ctx.send("You can't trade with yourself!") + return + + # Check if user has the item + inventory = await self.config.user(ctx.author).inventory() + if item_id not in inventory or inventory[item_id] < quantity: + await ctx.send("You don't have enough of that item!") + return + + # Check if either user is already trading + author_trading = await self.config.user(ctx.author).trading_with() + target_trading = await self.config.user(user).trading_with() + + if author_trading or target_trading: + await ctx.send("One of the users is already in a trade!") + return + + # Set up the trade + await self.config.user(ctx.author).trading_with.set(user.id) + await self.config.user(user).trading_with.set(ctx.author.id) + + await self.config.user(ctx.author).trade_offer.set({item_id: quantity}) + await self.config.user(ctx.author).trade_accepted.set(False) + await self.config.user(user).trade_accepted.set(False) + + guild_items = await self.config.guild(ctx.guild).items() + item_name = guild_items[item_id]["name"] + + await ctx.send(f"{user.mention}, {ctx.author.name} wants to trade {quantity}x {item_name} with you!\n" + f"Use `{ctx.prefix}trade accept` to accept or `{ctx.prefix}trade cancel` to decline.") + + @trade.command(name="accept") + async def trade_accept(self, ctx: commands.Context): + """Accept a trade offer""" + trading_with = await self.config.user(ctx.author).trading_with() + if not trading_with: + await ctx.send("You don't have any pending trades!") + return + + trader = ctx.guild.get_member(trading_with) + if not trader: + await ctx.send("Could not find the user you're trading with!") + await self.cancel_trade(ctx.author) + return + + # Get the trade details + trade_offer = await self.config.user(trader).trade_offer() + if not trade_offer: + await ctx.send("No items were offered for trade!") + await self.cancel_trade(ctx.author) + return + + # Process the trade + trader_inventory = await self.config.user(trader).inventory() + receiver_inventory = await self.config.user(ctx.author).inventory() + + for item_id, quantity in trade_offer.items(): + # Remove from trader + if trader_inventory[item_id] < quantity: + await ctx.send("The trader no longer has enough items!") + await self.cancel_trade(ctx.author) + return + + trader_inventory[item_id] -= quantity + if trader_inventory[item_id] <= 0: + del trader_inventory[item_id] + + # Add to receiver + if item_id in receiver_inventory: + receiver_inventory[item_id] += quantity + else: + receiver_inventory[item_id] = quantity + + # Save the new inventories + await self.config.user(trader).inventory.set(trader_inventory) + await self.config.user(ctx.author).inventory.set(receiver_inventory) + + # Record the trade in history + guild_items = await self.config.guild(ctx.guild).items() + trade_record = { + "from": trader.id, + "to": ctx.author.id, + "items": {item_id: {"name": guild_items[item_id]["name"], "quantity": qty} + for item_id, qty in trade_offer.items()} + } + + async with self.config.user(trader).trade_history() as history: + history.append(trade_record) + async with self.config.user(ctx.author).trade_history() as history: + history.append(trade_record) + + # Clear the trade + await self.cancel_trade(ctx.author) + await self.cancel_trade(trader) + + # Notify users + items_traded = [f"{qty}x {guild_items[item_id]['name']}" for item_id, qty in trade_offer.items()] + await ctx.send(f"Trade completed! {trader.name} traded {humanize_list(items_traded)} to {ctx.author.name}") + + @trade.command(name="cancel") + async def trade_cancel(self, ctx: commands.Context): + """Cancel a pending trade""" + trading_with = await self.config.user(ctx.author).trading_with() + if not trading_with: + await ctx.send("You don't have any pending trades!") + return + + other_user = ctx.guild.get_member(trading_with) + await self.cancel_trade(ctx.author) + if other_user: + await self.cancel_trade(other_user) + + await ctx.send("Trade cancelled!") + + async def cancel_trade(self, user): + """Helper function to cancel a trade for a user""" + await self.config.user(user).trading_with.set(None) + await self.config.user(user).trade_offer.set({}) + await self.config.user(user).trade_accepted.set(False) + + @commands.command() + async def iteminfo(self, ctx: commands.Context, item_id: str): + """Get information about an item""" + guild_items = await self.config.guild(ctx.guild).items() + if item_id not in guild_items: + await ctx.send(f"Invalid item ID! Available items: {', '.join(guild_items.keys())}") + return + + item = guild_items[item_id] + await ctx.send(box( + f"Item: {item['name']}\n" + f"Value: {item['value']} coins\n" + f"Description: {item['description']}" + )) + + @commands.command() + async def listitems(self, ctx: commands.Context): + """List all available items""" + guild_items = await self.config.guild(ctx.guild).items() + + output = ["Available Items:"] + for item_id, item in guild_items.items(): + output.append(f"{item_id}: {item['name']} - {item['value']} coins") + + await ctx.send(box("\n".join(output))) \ No newline at end of file diff --git a/inventory/requirements.txt b/inventory/requirements.txt new file mode 100644 index 0000000..2839fb9 --- /dev/null +++ b/inventory/requirements.txt @@ -0,0 +1,2 @@ +Red-DiscordBot>=3.5.0 +discord.py>=2.0.0 \ No newline at end of file