Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import discord
- from discord.ext import commands
- from discord import app_commands
- import json
- import os
- from datetime import datetime
- # Bot setup
- intents = discord.Intents.default()
- bot = commands.Bot(command_prefix="!", intents=intents)
- LOG_LIMIT = 50
- STORAGE_FILE = "ships_data.json"
- # Ship class
- class Ship:
- def __init__(self, ship_type, name, location, home_port, status, damage, regiment, keys,
- message_id=None, channel_id=None):
- self.ship_type = ship_type
- self.name = name
- self.location = location
- self.home_port = home_port
- self.status = status
- self.damage = int(damage)
- self.regiment = regiment
- self.keys = keys
- self.message_id = message_id
- self.channel_id = channel_id
- def to_embed(self):
- embed = discord.Embed(title=self.name, color=discord.Color.blue())
- embed.add_field(name="Type", value=self.ship_type, inline=True)
- embed.add_field(name="Status", value=self.status, inline=True)
- embed.add_field(name="Damage", value=f"{'💨' * self.damage}", inline=True)
- embed.add_field(name="Location", value=self.location, inline=True)
- embed.add_field(name="Home Port", value=self.home_port, inline=True)
- embed.add_field(name="Regiment", value=self.regiment, inline=True)
- embed.add_field(name="Keys", value=self.keys, inline=True)
- return embed
- # Persistent storage
- class ShipDataStore:
- def __init__(self, storage_path=STORAGE_FILE):
- self.storage_path = storage_path
- self.ships = {}
- self.logs = {}
- self.load()
- def add_ship(self, ship):
- self.ships[ship.name] = ship
- self.logs.setdefault(ship.name, [])
- def remove_ship(self, name):
- if name in self.ships:
- del self.ships[name]
- if name in self.logs:
- del self.logs[name]
- self.save()
- def log_action(self, ship_name, user, action):
- if ship_name not in self.logs:
- self.logs[ship_name] = []
- timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')
- self.logs[ship_name].append(f"[{timestamp}] {user}: {action}")
- self.logs[ship_name] = self.logs[ship_name][-LOG_LIMIT:]
- self.save()
- def get_logs(self, ship_name):
- return self.logs.get(ship_name, [])
- def save(self):
- data = {
- "ships": {name: vars(ship) for name, ship in self.ships.items()},
- "logs": self.logs
- }
- with open(self.storage_path, "w") as f:
- json.dump(data, f, indent=4)
- def load(self):
- if not os.path.exists(self.storage_path):
- return
- with open(self.storage_path, "r") as f:
- data = json.load(f)
- for name, values in data.get("ships", {}).items():
- ship = Ship(**values)
- self.ships[name] = ship
- self.logs = data.get("logs", {})
- data_store = ShipDataStore()
- # Interactive button view
- class ShipView(discord.ui.View):
- def __init__(self, ship: Ship):
- super().__init__(timeout=None)
- self.ship = ship
- @discord.ui.button(label="Departing", style=discord.ButtonStyle.success)
- async def departing(self, interaction: discord.Interaction, button: discord.ui.Button):
- self.ship.status = "Deployed" if button.label == "Departing" else "Parked"
- button.label = "Returned" if button.label == "Departing" else "Departing"
- await self.update(interaction, f"Status changed to {self.ship.status}")
- @discord.ui.button(label="Repair", style=discord.ButtonStyle.primary)
- async def repair(self, interaction: discord.Interaction, button: discord.ui.Button):
- self.ship.status = "Repairing" if button.label == "Repair" else "Parked"
- button.label = "Finish Repairs" if button.label == "Repair" else "Repair"
- await self.update(interaction, f"Status changed to {self.ship.status}")
- @discord.ui.button(label="Dead", style=discord.ButtonStyle.danger)
- async def dead(self, interaction: discord.Interaction, button: discord.ui.Button):
- self.ship.status = "Dead"
- await self.update(interaction, f"Marked as Dead")
- async def update(self, interaction, action_desc):
- data_store.log_action(self.ship.name, interaction.user.name, action_desc)
- channel = interaction.client.get_channel(self.ship.channel_id)
- message = await channel.fetch_message(self.ship.message_id)
- await message.edit(embed=self.ship.to_embed(), view=self)
- await interaction.response.defer()
- data_store.save()
- # Continuing from previous code...
- @bot.event
- async def on_ready():
- print(f"Logged in as {bot.user}")
- try:
- synced = await bot.tree.sync()
- print(f"Synced {len(synced)} commands")
- except Exception as e:
- print(f"Failed to sync commands: {e}")
- # Helper function to get ship by name or respond with error
- async def get_ship_or_error(interaction, ship_name):
- ship = data_store.ships.get(ship_name)
- if not ship:
- await interaction.response.send_message(f"Ship `{ship_name}` not found.", ephemeral=True)
- return None
- return ship
- # /create_ship command
- @bot.tree.command(name="create_ship", description="Create a new ship record")
- @app_commands.describe(
- ship_type="Type of ship (destroyer, submarine, battleship, bluefin)",
- name="Name of the ship",
- location="Current location of the ship",
- home_port="Ship's home port",
- status="Current status (Deployed, Parked, Repairing, Dead)",
- damage="Damage count (0-5)",
- regiment="Regiment the ship belongs to",
- keys="Squad keys"
- )
- @app_commands.checks.has_permissions(administrator=True)
- async def create_ship(interaction: discord.Interaction, ship_type: str, name: str, location: str,
- home_port: str, status: str, damage: int, regiment: str, keys: str):
- if name in data_store.ships:
- await interaction.response.send_message(f"A ship named `{name}` already exists.", ephemeral=True)
- return
- # Validate damage
- if not 0 <= damage <= 5:
- await interaction.response.send_message("Damage must be between 0 and 5.", ephemeral=True)
- return
- ship = Ship(ship_type, name, location, home_port, status, damage, regiment, keys)
- view = ShipView(ship)
- # Send message with embed and buttons
- message = await interaction.channel.send(embed=ship.to_embed(), view=view)
- ship.message_id = message.id
- ship.channel_id = message.channel.id
- data_store.add_ship(ship)
- data_store.save()
- data_store.log_action(name, interaction.user.name, "Created ship")
- await interaction.response.send_message(f"Ship `{name}` created successfully.", ephemeral=True)
- # /update_ship command
- @bot.tree.command(name="update_ship", description="Update a ship's field")
- @app_commands.describe(
- name="Name of the ship",
- field="Field to update (Location, Home Port, Status, Damage, Keys)",
- data="New value for the field"
- )
- @app_commands.checks.has_permissions(administrator=True)
- async def update_ship(interaction: discord.Interaction, name: str, field: str, data: str):
- ship = await get_ship_or_error(interaction, name)
- if not ship:
- return
- field = field.lower()
- valid_fields = ['location', 'home port', 'status', 'damage', 'keys']
- if field not in [f.lower() for f in valid_fields]:
- await interaction.response.send_message(f"Invalid field `{field}`. Valid fields: {', '.join(valid_fields)}", ephemeral=True)
- return
- if field == "damage":
- try:
- damage_val = int(data)
- if not 0 <= damage_val <= 5:
- raise ValueError()
- ship.damage = damage_val
- except ValueError:
- await interaction.response.send_message("Damage must be an integer between 0 and 5.", ephemeral=True)
- return
- elif field == "home port":
- ship.home_port = data
- elif field == "location":
- ship.location = data
- elif field == "status":
- ship.status = data
- elif field == "keys":
- ship.keys = data
- data_store.log_action(name, interaction.user.name, f"Updated {field} to {data}")
- # Update embed in message
- channel = bot.get_channel(ship.channel_id)
- if channel:
- try:
- message = await channel.fetch_message(ship.message_id)
- await message.edit(embed=ship.to_embed(), view=ShipView(ship))
- except:
- pass
- data_store.save()
- await interaction.response.send_message(f"Updated `{field}` of ship `{name}`.", ephemeral=True)
- # /remove_ship command
- @bot.tree.command(name="remove_ship", description="Remove a ship from tracking")
- @app_commands.describe(name="Name of the ship to remove")
- @app_commands.checks.has_permissions(administrator=True)
- async def remove_ship(interaction: discord.Interaction, name: str):
- if name not in data_store.ships:
- await interaction.response.send_message(f"No ship named `{name}` found.", ephemeral=True)
- return
- data_store.remove_ship(name)
- await interaction.response.send_message(f"Ship `{name}` removed.", ephemeral=True)
- # /list_ships command
- @bot.tree.command(name="list_ships", description="List all tracked ships")
- async def list_ships(interaction: discord.Interaction):
- if not data_store.ships:
- await interaction.response.send_message("No ships are currently being tracked.", ephemeral=True)
- return
- ships_list = "\n".join(f"- {name} ({ship.ship_type}) - Status: {ship.status}" for name, ship in data_store.ships.items())
- await interaction.response.send_message(f"Tracked ships:\n{ships_list}", ephemeral=True)
- # /view_logs command
- @bot.tree.command(name="view_logs", description="View last 50 logs of a ship")
- @app_commands.describe(name="Name of the ship")
- @app_commands.checks.has_permissions(administrator=True)
- async def view_logs(interaction: discord.Interaction, name: str):
- if name not in data_store.ships:
- await interaction.response.send_message(f"No ship named `{name}` found.", ephemeral=True)
- return
- logs = data_store.get_logs(name)
- if not logs:
- await interaction.response.send_message(f"No logs found for ship `{name}`.", ephemeral=True)
- return
- # Format logs, splitting into multiple messages if too long
- MAX_CHARS = 1900
- log_text = "\n".join(logs)
- messages = []
- while log_text:
- if len(log_text) <= MAX_CHARS:
- messages.append(log_text)
- break
- else:
- split_index = log_text.rfind("\n", 0, MAX_CHARS)
- if split_index == -1:
- split_index = MAX_CHARS
- messages.append(log_text[:split_index])
- log_text = log_text[split_index:].lstrip("\n")
- await interaction.response.send_message(f"Logs for ship `{name}`:", ephemeral=True)
- for part in messages:
- await interaction.followup.send(f"```\n{part}\n```", ephemeral=True)
- bot.run("MTM1OTAyOTM1OTY1ODI3MDg2MQ.G8uvpN.BrzUjaS2DPFhKlm5X4HejxrVcMpPI3u-ukJRko")
Advertisement
Add Comment
Please, Sign In to add comment