Standardize data file locations to data/ directory and improve command organization with better UI for team rosters, pagination for team lists, and refactored chart commands into logical command groups. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
185 lines
6.1 KiB
Python
185 lines
6.1 KiB
Python
"""
|
|
Trade Channel Tracker
|
|
|
|
Provides persistent tracking of bot-created trade discussion channels using JSON file storage.
|
|
"""
|
|
import json
|
|
import logging
|
|
from datetime import datetime, UTC
|
|
from pathlib import Path
|
|
from typing import Dict, List, Optional, Any
|
|
|
|
import discord
|
|
|
|
from utils.logging import get_contextual_logger
|
|
|
|
logger = get_contextual_logger(f'{__name__}.TradeChannelTracker')
|
|
|
|
|
|
class TradeChannelTracker:
|
|
"""
|
|
Tracks bot-created trade discussion channels with JSON file persistence.
|
|
|
|
Features:
|
|
- Persistent storage across bot restarts
|
|
- Channel creation and tracking by trade ID
|
|
- Lookup by trade ID or channel ID
|
|
- Automatic stale entry removal
|
|
"""
|
|
|
|
def __init__(self, data_file: str = "data/trade_channels.json"):
|
|
"""
|
|
Initialize the trade channel tracker.
|
|
|
|
Args:
|
|
data_file: Path to the JSON data file
|
|
"""
|
|
self.data_file = Path(data_file)
|
|
self.data_file.parent.mkdir(exist_ok=True)
|
|
self._data: Dict[str, Any] = {}
|
|
self.load_data()
|
|
|
|
def load_data(self) -> None:
|
|
"""Load channel data from JSON file."""
|
|
try:
|
|
if self.data_file.exists():
|
|
with open(self.data_file, 'r') as f:
|
|
self._data = json.load(f)
|
|
logger.debug(f"Loaded {len(self._data.get('trade_channels', {}))} tracked trade channels")
|
|
else:
|
|
self._data = {"trade_channels": {}}
|
|
logger.info("No existing trade channel data found, starting fresh")
|
|
except Exception as e:
|
|
logger.error(f"Failed to load trade channel data: {e}")
|
|
self._data = {"trade_channels": {}}
|
|
|
|
def save_data(self) -> None:
|
|
"""Save channel data to JSON file."""
|
|
try:
|
|
with open(self.data_file, 'w') as f:
|
|
json.dump(self._data, f, indent=2, default=str)
|
|
logger.debug("Trade channel data saved successfully")
|
|
except Exception as e:
|
|
logger.error(f"Failed to save trade channel data: {e}")
|
|
|
|
def add_channel(
|
|
self,
|
|
channel: discord.TextChannel,
|
|
trade_id: str,
|
|
team1_abbrev: str,
|
|
team2_abbrev: str,
|
|
creator_id: int
|
|
) -> None:
|
|
"""
|
|
Add a new trade channel to tracking.
|
|
|
|
Args:
|
|
channel: Discord text channel object
|
|
trade_id: Unique trade identifier
|
|
team1_abbrev: First team abbreviation
|
|
team2_abbrev: Second team abbreviation
|
|
creator_id: Discord user ID who created the trade
|
|
"""
|
|
self._data.setdefault("trade_channels", {})[str(channel.id)] = {
|
|
"channel_id": str(channel.id),
|
|
"guild_id": str(channel.guild.id),
|
|
"name": channel.name,
|
|
"trade_id": trade_id,
|
|
"team1_abbrev": team1_abbrev,
|
|
"team2_abbrev": team2_abbrev,
|
|
"created_at": datetime.now(UTC).isoformat(),
|
|
"creator_id": str(creator_id)
|
|
}
|
|
self.save_data()
|
|
logger.info(f"Added trade channel to tracking: {channel.name} (ID: {channel.id}, Trade: {trade_id})")
|
|
|
|
def remove_channel(self, channel_id: int) -> None:
|
|
"""
|
|
Remove channel from tracking.
|
|
|
|
Args:
|
|
channel_id: Discord channel ID
|
|
"""
|
|
channels = self._data.get("trade_channels", {})
|
|
channel_key = str(channel_id)
|
|
|
|
if channel_key in channels:
|
|
channel_data = channels[channel_key]
|
|
trade_id = channel_data.get("trade_id", "unknown")
|
|
channel_name = channel_data["name"]
|
|
del channels[channel_key]
|
|
self.save_data()
|
|
logger.info(f"Removed trade channel from tracking: {channel_name} (ID: {channel_id}, Trade: {trade_id})")
|
|
|
|
def get_channel_by_trade_id(self, trade_id: str) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Get channel data for a specific trade.
|
|
|
|
Args:
|
|
trade_id: Trade identifier
|
|
|
|
Returns:
|
|
Channel data dictionary or None if not found
|
|
"""
|
|
channels = self._data.get("trade_channels", {})
|
|
for channel_data in channels.values():
|
|
if channel_data.get("trade_id") == trade_id:
|
|
return channel_data
|
|
return None
|
|
|
|
def get_channel_by_id(self, channel_id: int) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Get data for a specific tracked channel.
|
|
|
|
Args:
|
|
channel_id: Discord channel ID
|
|
|
|
Returns:
|
|
Channel data dictionary or None if not tracked
|
|
"""
|
|
channels = self._data.get("trade_channels", {})
|
|
return channels.get(str(channel_id))
|
|
|
|
def get_all_tracked_channels(self) -> List[Dict[str, Any]]:
|
|
"""
|
|
Get all currently tracked trade channels.
|
|
|
|
Returns:
|
|
List of all tracked channel data dictionaries
|
|
"""
|
|
return list(self._data.get("trade_channels", {}).values())
|
|
|
|
def cleanup_stale_entries(self, valid_channel_ids: List[int]) -> int:
|
|
"""
|
|
Remove tracking entries for channels that no longer exist.
|
|
|
|
Args:
|
|
valid_channel_ids: List of channel IDs that still exist in Discord
|
|
|
|
Returns:
|
|
Number of stale entries removed
|
|
"""
|
|
channels = self._data.get("trade_channels", {})
|
|
stale_entries = []
|
|
|
|
for channel_id_str, channel_data in channels.items():
|
|
try:
|
|
channel_id = int(channel_id_str)
|
|
if channel_id not in valid_channel_ids:
|
|
stale_entries.append(channel_id_str)
|
|
except (ValueError, TypeError):
|
|
logger.warning(f"Invalid channel ID in tracking data: {channel_id_str}")
|
|
stale_entries.append(channel_id_str)
|
|
|
|
# Remove stale entries
|
|
for channel_id_str in stale_entries:
|
|
channel_name = channels[channel_id_str].get("name", "unknown")
|
|
trade_id = channels[channel_id_str].get("trade_id", "unknown")
|
|
del channels[channel_id_str]
|
|
logger.info(f"Removed stale tracking entry: {channel_name} (ID: {channel_id_str}, Trade: {trade_id})")
|
|
|
|
if stale_entries:
|
|
self.save_data()
|
|
|
|
return len(stale_entries)
|