Compare commits

...

2 commits

5 changed files with 346 additions and 40 deletions

8
inventory/__init__.py Normal file
View file

@ -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)

17
inventory/info.json Normal file
View file

@ -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."
}

245
inventory/inventory.py Normal file
View file

@ -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)))

View file

@ -0,0 +1,2 @@
Red-DiscordBot>=3.5.0
discord.py>=2.0.0

View file

@ -29,11 +29,15 @@ class TranslatorCog(commands.Cog):
await self.config.guild(ctx.guild).use_embeds.set(toggle)
await ctx.send(f"Embed display has been {'enabled' if toggle else 'disabled'}.")
async def translate_and_respond(self, ctx, text_to_translate, dest_lang="en", source_lang="auto", original_author=None, ephemeral=False):
async def translate_and_respond(self, ctx, text_to_translate, dest_lang="en", source_lang="auto", original_author=None, ephemeral=False, reference_message=None):
try:
# Validate destination language
if dest_lang.lower() not in self.languages and dest_lang.lower() not in {v.lower(): k for k, v in self.languages.items()}:
return await ctx.send(f"Invalid destination language code. Use `{ctx.prefix}langlist` to see available languages.", ephemeral=True)
return await ctx.send(
f"Invalid language code. Use `{ctx.prefix}langlist` to see available languages.",
ephemeral=True,
reference=reference_message
)
# Convert language name to code if full name was provided
if dest_lang.lower() in {v.lower(): k for k, v in self.languages.items()}:
@ -50,7 +54,12 @@ class TranslatorCog(commands.Cog):
embed.set_author(name=f"Original message by {original_author.display_name}", icon_url=original_author.display_avatar.url)
embed.add_field(name=f"Original ({translation.src}):", value=text_to_translate[:1024], inline=False)
embed.add_field(name=f"Translation ({translation.dest}):", value=translation.text[:1024], inline=False)
await ctx.send(content=ctx.author.mention, embed=embed, ephemeral=ephemeral)
await ctx.send(
content=ctx.author.mention,
embed=embed,
ephemeral=ephemeral,
reference=reference_message
)
else:
author_text = f"\nOriginal message by: {original_author.display_name}" if original_author else ""
response = (
@ -58,58 +67,83 @@ class TranslatorCog(commands.Cog):
f"**Original** ({translation.src}):{author_text}\n{text_to_translate}\n\n"
f"**Translation** ({translation.dest}):\n{translation.text}"
)
await ctx.send(response, ephemeral=ephemeral)
await ctx.send(
response,
ephemeral=ephemeral,
reference=reference_message
)
except Exception as e:
await ctx.send(f"An error occurred while translating: {str(e)}", ephemeral=True)
await ctx.send(
f"An error occurred while translating: {str(e)}",
ephemeral=True,
reference=reference_message
)
@commands.command(name="translate")
async def translate_text(self, ctx, *, message=None):
"""Translate a message to English by replying to it
You can also specify a language code after your message to translate to that language
Example: [p]translate bonjour fr -> en
async def translate_text(self, ctx, lang_code: str = "en", *, message=None):
"""Translate a message
Reply to a message with just the language code to translate to that language
Or provide text after the language code to translate that text
Examples:
- Reply to a message: [p]translate jp
- Translate text: [p]translate fr Hello, how are you?
- Auto-detect and translate to English: [p]translate This will detect the language
"""
if not message and not ctx.message.reference:
await ctx.send("Please either provide text to translate or reply to a message to translate it.")
return
# Initialize variables
text_to_translate = None
original_author = None
reference_message = None
# If replying to a message
if ctx.message.reference:
referenced_msg = await ctx.fetch_message(ctx.message.reference.message_id)
text_to_translate = referenced_msg.content
original_author = referenced_msg.author
# Check if language was specified in the command
if message:
try:
source_lang, dest_lang = message.split("->")
source_lang = source_lang.strip()
dest_lang = dest_lang.strip()
except ValueError:
dest_lang = "en"
source_lang = "auto"
reference_message = referenced_msg
# If no language code is provided in a reply, default to English
if not lang_code or lang_code.lower() not in self.languages:
if message: # If there's additional text, it means the first argument was text, not a lang code
text_to_translate = f"{lang_code} {message}"
lang_code = "en"
else:
dest_lang = "en"
source_lang = "auto"
else:
original_author = ctx.author
# Direct message translation
if "->" in message:
try:
text, langs = message.rsplit("->", 1)
text_to_translate = text.strip()
dest_lang = langs.strip()
source_lang = "auto"
except ValueError:
# Valid language code provided
if message: # If there's a message after the lang code, use that instead of the replied message
text_to_translate = message
dest_lang = "en"
source_lang = "auto"
original_author = ctx.author
reference_message = ctx.message
else:
# Not replying to a message
original_author = ctx.author
reference_message = ctx.message
if not message: # Only language code provided without text
await ctx.send("Please provide text to translate or reply to a message.", reference=ctx.message)
return
# Check if the "lang_code" is actually the start of the message
if lang_code.lower() not in self.languages:
text_to_translate = f"{lang_code} {message}" if message else lang_code
lang_code = "en" # Default to English for direct translation
else:
text_to_translate = message
dest_lang = "en"
source_lang = "auto"
await self.translate_and_respond(ctx, text_to_translate, dest_lang, source_lang, original_author)
# If we still don't have text to translate
if not text_to_translate:
await ctx.send("Please provide text to translate or reply to a message.", reference=ctx.message)
return
await self.translate_and_respond(
ctx,
text_to_translate,
dest_lang=lang_code,
source_lang="auto",
original_author=original_author,
reference_message=reference_message
)
@commands.command(name="langlist")
async def language_list(self, ctx):
@ -128,7 +162,7 @@ class TranslatorCog(commands.Cog):
color=discord.Color.blue())
else:
embed = discord.Embed(description="\n".join(chunk), color=discord.Color.blue())
await ctx.send(embed=embed)
await ctx.send(embed=embed, reference=ctx.message if i == 0 else None)
@app_commands.command(name="translate")
@app_commands.describe(