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 self.config.guild(ctx.guild).use_embeds.set(toggle)
|
||||||
await ctx.send(f"Embed display has been {'enabled' if toggle else 'disabled'}.")
|
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:
|
try:
|
||||||
# Validate destination language
|
# 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()}:
|
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
|
# 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()}:
|
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.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"Original ({translation.src}):", value=text_to_translate[:1024], inline=False)
|
||||||
embed.add_field(name=f"Translation ({translation.dest}):", value=translation.text[: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:
|
else:
|
||||||
author_text = f"\nOriginal message by: {original_author.display_name}" if original_author else ""
|
author_text = f"\nOriginal message by: {original_author.display_name}" if original_author else ""
|
||||||
response = (
|
response = (
|
||||||
|
@ -58,58 +67,83 @@ class TranslatorCog(commands.Cog):
|
||||||
f"**Original** ({translation.src}):{author_text}\n{text_to_translate}\n\n"
|
f"**Original** ({translation.src}):{author_text}\n{text_to_translate}\n\n"
|
||||||
f"**Translation** ({translation.dest}):\n{translation.text}"
|
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:
|
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")
|
@commands.command(name="translate")
|
||||||
async def translate_text(self, ctx, *, message=None):
|
async def translate_text(self, ctx, lang_code: str = "en", *, message=None):
|
||||||
"""Translate a message to English by replying to it
|
"""Translate a message
|
||||||
You can also specify a language code after your message to translate to that language
|
Reply to a message with just the language code to translate to that language
|
||||||
Example: [p]translate bonjour fr -> en
|
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:
|
# Initialize variables
|
||||||
await ctx.send("Please either provide text to translate or reply to a message to translate it.")
|
text_to_translate = None
|
||||||
return
|
original_author = None
|
||||||
|
reference_message = None
|
||||||
|
|
||||||
# If replying to a message
|
# If replying to a message
|
||||||
if ctx.message.reference:
|
if ctx.message.reference:
|
||||||
referenced_msg = await ctx.fetch_message(ctx.message.reference.message_id)
|
referenced_msg = await ctx.fetch_message(ctx.message.reference.message_id)
|
||||||
text_to_translate = referenced_msg.content
|
text_to_translate = referenced_msg.content
|
||||||
original_author = referenced_msg.author
|
original_author = referenced_msg.author
|
||||||
|
reference_message = referenced_msg
|
||||||
# Check if language was specified in the command
|
|
||||||
if message:
|
# If no language code is provided in a reply, default to English
|
||||||
try:
|
if not lang_code or lang_code.lower() not in self.languages:
|
||||||
source_lang, dest_lang = message.split("->")
|
if message: # If there's additional text, it means the first argument was text, not a lang code
|
||||||
source_lang = source_lang.strip()
|
text_to_translate = f"{lang_code} {message}"
|
||||||
dest_lang = dest_lang.strip()
|
lang_code = "en"
|
||||||
except ValueError:
|
|
||||||
dest_lang = "en"
|
|
||||||
source_lang = "auto"
|
|
||||||
else:
|
else:
|
||||||
dest_lang = "en"
|
# Valid language code provided
|
||||||
source_lang = "auto"
|
if message: # If there's a message after the lang code, use that instead of the replied message
|
||||||
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:
|
|
||||||
text_to_translate = message
|
text_to_translate = message
|
||||||
dest_lang = "en"
|
original_author = ctx.author
|
||||||
source_lang = "auto"
|
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:
|
else:
|
||||||
text_to_translate = message
|
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")
|
@commands.command(name="langlist")
|
||||||
async def language_list(self, ctx):
|
async def language_list(self, ctx):
|
||||||
|
@ -128,7 +162,7 @@ class TranslatorCog(commands.Cog):
|
||||||
color=discord.Color.blue())
|
color=discord.Color.blue())
|
||||||
else:
|
else:
|
||||||
embed = discord.Embed(description="\n".join(chunk), color=discord.Color.blue())
|
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.command(name="translate")
|
||||||
@app_commands.describe(
|
@app_commands.describe(
|
||||||
|
|
Loading…
Add table
Reference in a new issue