Add multi-channel support for stock updates
Bot now supports sending updates to multiple Discord channels simultaneously. Changes: - CHANNEL_ID accepts comma-separated list of channel IDs - Config parses and validates multiple channel IDs - Hourly and market close updates sent to all configured channels - Update .env.example to show multi-channel format - Document multi-channel configuration in README Example: CHANNEL_ID=1442203998932304035,9876543210123456789 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Discord Configuration
|
# Discord Configuration
|
||||||
DISCORD_TOKEN=your_discord_bot_token_here
|
DISCORD_TOKEN=your_discord_bot_token_here
|
||||||
CHANNEL_ID=your_channel_id_here
|
CHANNEL_ID=your_channel_id_here,optional_second_channel_id
|
||||||
|
|
||||||
# Bot Configuration
|
# Bot Configuration
|
||||||
COMMAND_PREFIX=!
|
COMMAND_PREFIX=!
|
||||||
|
|||||||
@@ -159,11 +159,16 @@ Environment variables in `.env`:
|
|||||||
| Variable | Description | Default |
|
| Variable | Description | Default |
|
||||||
|----------|-------------|---------|
|
|----------|-------------|---------|
|
||||||
| `DISCORD_TOKEN` | Your Discord bot token | Required |
|
| `DISCORD_TOKEN` | Your Discord bot token | Required |
|
||||||
| `CHANNEL_ID` | Discord channel ID for updates | Required |
|
| `CHANNEL_ID` | Discord channel ID(s) for updates (comma-separated for multiple) | Required |
|
||||||
| `COMMAND_PREFIX` | Command prefix for bot | `!` |
|
| `COMMAND_PREFIX` | Command prefix for bot | `!` |
|
||||||
| `PRIMARY_TICKER` | Stock ticker for hourly updates | `PYPL` |
|
| `PRIMARY_TICKER` | Stock ticker for hourly updates | `PYPL` |
|
||||||
| `UPDATE_INTERVAL_HOURS` | Hours between updates | `1` |
|
| `UPDATE_INTERVAL_HOURS` | Hours between updates | `1` |
|
||||||
|
|
||||||
|
**Multi-Channel Support:** To send updates to multiple channels, provide comma-separated channel IDs:
|
||||||
|
```
|
||||||
|
CHANNEL_ID=1442203998932304035,9876543210123456789
|
||||||
|
```
|
||||||
|
|
||||||
## Switching Stock API Providers
|
## Switching Stock API Providers
|
||||||
|
|
||||||
The bot is designed with an abstract API layer. To switch providers:
|
The bot is designed with an abstract API layer. To switch providers:
|
||||||
|
|||||||
23
bot.py
23
bot.py
@@ -36,7 +36,7 @@ class StockBot(commands.Bot):
|
|||||||
logger.info("Using Yahoo Finance API for stock data")
|
logger.info("Using Yahoo Finance API for stock data")
|
||||||
|
|
||||||
self.scheduler = AsyncIOScheduler(timezone=pytz.timezone('America/New_York'))
|
self.scheduler = AsyncIOScheduler(timezone=pytz.timezone('America/New_York'))
|
||||||
self.target_channel_id = int(Config.CHANNEL_ID)
|
self.target_channel_ids = [int(id) for id in Config.CHANNEL_IDS]
|
||||||
self.primary_ticker = Config.PRIMARY_TICKER
|
self.primary_ticker = Config.PRIMARY_TICKER
|
||||||
|
|
||||||
async def setup_hook(self):
|
async def setup_hook(self):
|
||||||
@@ -67,14 +67,15 @@ class StockBot(commands.Bot):
|
|||||||
"""Called when bot successfully connects to Discord."""
|
"""Called when bot successfully connects to Discord."""
|
||||||
logger.info(f'Bot connected as {self.user.name} (ID: {self.user.id})')
|
logger.info(f'Bot connected as {self.user.name} (ID: {self.user.id})')
|
||||||
logger.info(f'Monitoring ticker: {self.primary_ticker}')
|
logger.info(f'Monitoring ticker: {self.primary_ticker}')
|
||||||
logger.info(f'Target channel ID: {self.target_channel_id}')
|
logger.info(f'Target channel IDs: {self.target_channel_ids}')
|
||||||
|
|
||||||
# Verify channel exists
|
# Verify channels exist
|
||||||
channel = self.get_channel(self.target_channel_id)
|
for channel_id in self.target_channel_ids:
|
||||||
if channel:
|
channel = self.get_channel(channel_id)
|
||||||
logger.info(f'Target channel found: #{channel.name}')
|
if channel:
|
||||||
else:
|
logger.info(f'Target channel found: #{channel.name} (ID: {channel_id})')
|
||||||
logger.error(f'Could not find channel with ID {self.target_channel_id}')
|
else:
|
||||||
|
logger.error(f'Could not find channel with ID {channel_id}')
|
||||||
|
|
||||||
async def send_hourly_update(self):
|
async def send_hourly_update(self):
|
||||||
"""Send hourly stock price update if market is open."""
|
"""Send hourly stock price update if market is open."""
|
||||||
@@ -83,7 +84,8 @@ class StockBot(commands.Bot):
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"Sending hourly update for {self.primary_ticker}")
|
logger.info(f"Sending hourly update for {self.primary_ticker}")
|
||||||
await self.send_stock_update(self.primary_ticker, self.target_channel_id)
|
for channel_id in self.target_channel_ids:
|
||||||
|
await self.send_stock_update(self.primary_ticker, channel_id)
|
||||||
|
|
||||||
async def send_market_close_update(self):
|
async def send_market_close_update(self):
|
||||||
"""Send stock price update at market close on trading days."""
|
"""Send stock price update at market close on trading days."""
|
||||||
@@ -92,7 +94,8 @@ class StockBot(commands.Bot):
|
|||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"Sending market close update for {self.primary_ticker}")
|
logger.info(f"Sending market close update for {self.primary_ticker}")
|
||||||
await self.send_stock_update(self.primary_ticker, self.target_channel_id)
|
for channel_id in self.target_channel_ids:
|
||||||
|
await self.send_stock_update(self.primary_ticker, channel_id)
|
||||||
|
|
||||||
async def send_stock_update(self, ticker: str, channel_id: int):
|
async def send_stock_update(self, ticker: str, channel_id: int):
|
||||||
"""
|
"""
|
||||||
|
|||||||
10
config.py
10
config.py
@@ -10,6 +10,7 @@ class Config:
|
|||||||
# Discord configuration
|
# Discord configuration
|
||||||
DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
|
DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
|
||||||
CHANNEL_ID = os.getenv('CHANNEL_ID')
|
CHANNEL_ID = os.getenv('CHANNEL_ID')
|
||||||
|
CHANNEL_IDS = [id.strip() for id in os.getenv('CHANNEL_ID', '').split(',') if id.strip()]
|
||||||
COMMAND_PREFIX = os.getenv('COMMAND_PREFIX', '!')
|
COMMAND_PREFIX = os.getenv('COMMAND_PREFIX', '!')
|
||||||
|
|
||||||
# Stock configuration
|
# Stock configuration
|
||||||
@@ -36,10 +37,15 @@ class Config:
|
|||||||
print("ERROR: CHANNEL_ID not set in environment")
|
print("ERROR: CHANNEL_ID not set in environment")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not cls.CHANNEL_IDS:
|
||||||
|
print("ERROR: No valid channel IDs found in CHANNEL_ID")
|
||||||
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
int(cls.CHANNEL_ID)
|
for channel_id in cls.CHANNEL_IDS:
|
||||||
|
int(channel_id)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print("ERROR: CHANNEL_ID must be a numeric Discord channel ID")
|
print("ERROR: CHANNEL_ID must contain numeric Discord channel IDs (comma-separated)")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if cls.STOCK_API_PROVIDER == 'finnhub' and not cls.FINNHUB_API_KEY:
|
if cls.STOCK_API_PROVIDER == 'finnhub' and not cls.FINNHUB_API_KEY:
|
||||||
|
|||||||
Reference in New Issue
Block a user