Compare commits
2 commits
07686d47c2
...
2ac41b8486
Author | SHA1 | Date | |
---|---|---|---|
2ac41b8486 | |||
b8911e1b93 |
5 changed files with 346 additions and 40 deletions
8
inventory/__init__.py
Normal file
8
inventory/__init__.py
Normal 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
17
inventory/info.json
Normal 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
245
inventory/inventory.py
Normal 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)))
|
2
inventory/requirements.txt
Normal file
2
inventory/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Red-DiscordBot>=3.5.0
|
||||
discord.py>=2.0.0
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Reference in a new issue