add quiet/unquiet commands to suppress song change announcements
This commit is contained in:
parent
9c978c4773
commit
d7af7baa61
15
README.md
15
README.md
@ -73,6 +73,19 @@ admin:
|
||||
|
||||
## Usage
|
||||
|
||||
## Manager Commands
|
||||
|
||||
The bot manager supports the following commands:
|
||||
|
||||
```bash
|
||||
icecast-irc-bot-manager list # List running bots
|
||||
icecast-irc-bot-manager start --config FILE # Start a bot
|
||||
icecast-irc-bot-manager stop BOT_ID # Stop a bot
|
||||
icecast-irc-bot-manager restart BOT_ID # Restart a bot
|
||||
icecast-irc-bot-manager quiet BOT_ID # Disable song announcements
|
||||
icecast-irc-bot-manager unquiet BOT_ID # Enable song announcements
|
||||
```
|
||||
|
||||
### Running with Automatic Restart Support
|
||||
|
||||
The recommended way to run the bot is using the provided `bot.sh` script, which handles automatic restarts when using the `!restart` command:
|
||||
@ -132,6 +145,8 @@ Admin commands (only available to users listed in the `admin.users` config):
|
||||
|
||||
- `!start` - Start stream monitoring
|
||||
- `!stop` - Stop stream monitoring
|
||||
- `!quiet` - Disable song announcements but continue monitoring
|
||||
- `!unquiet` - Enable song announcements
|
||||
- `!reconnect` - Reconnect to the stream
|
||||
- `!restart` - Restart the bot
|
||||
- `!quit` - Shutdown the bot
|
||||
|
||||
@ -17,6 +17,7 @@ announce:
|
||||
- "Unknown"
|
||||
- "Unable to fetch metadata"
|
||||
- "Error fetching metadata"
|
||||
quiet_on_start: false # If true, bot starts in quiet mode (no announcements)
|
||||
|
||||
commands:
|
||||
prefix: "!" # Command prefix (e.g. !np, !help)
|
||||
|
||||
@ -388,6 +388,76 @@ class BotManager:
|
||||
# Start the bot with the same config
|
||||
return await self.start_bot(config_path)
|
||||
|
||||
async def quiet_bot(self, bot_id: str) -> bool:
|
||||
"""Disable song announcements for a running bot.
|
||||
|
||||
Args:
|
||||
bot_id: ID of the bot to quiet
|
||||
|
||||
Returns:
|
||||
bool: True if command was sent successfully
|
||||
"""
|
||||
# Check if bot exists in state
|
||||
state = self._load_state()
|
||||
if bot_id not in state:
|
||||
print(f"Bot {bot_id} not found")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Get the socket path for this bot
|
||||
socket_path = Path(tempfile.gettempdir()) / f"icecast_bot_{bot_id}.sock"
|
||||
if not socket_path.exists():
|
||||
print(f"Socket for bot {bot_id} not found at {socket_path}")
|
||||
return False
|
||||
|
||||
# Send quiet command
|
||||
print(f"Sending quiet command to bot {bot_id}")
|
||||
reader, writer = await asyncio.open_unix_connection(str(socket_path))
|
||||
writer.write(b'quiet')
|
||||
await writer.drain()
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
print(f"Quiet command sent to bot {bot_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error sending quiet command to bot {bot_id}: {e}")
|
||||
return False
|
||||
|
||||
async def unquiet_bot(self, bot_id: str) -> bool:
|
||||
"""Enable song announcements for a running bot.
|
||||
|
||||
Args:
|
||||
bot_id: ID of the bot to unquiet
|
||||
|
||||
Returns:
|
||||
bool: True if command was sent successfully
|
||||
"""
|
||||
# Check if bot exists in state
|
||||
state = self._load_state()
|
||||
if bot_id not in state:
|
||||
print(f"Bot {bot_id} not found")
|
||||
return False
|
||||
|
||||
try:
|
||||
# Get the socket path for this bot
|
||||
socket_path = Path(tempfile.gettempdir()) / f"icecast_bot_{bot_id}.sock"
|
||||
if not socket_path.exists():
|
||||
print(f"Socket for bot {bot_id} not found at {socket_path}")
|
||||
return False
|
||||
|
||||
# Send unquiet command
|
||||
print(f"Sending unquiet command to bot {bot_id}")
|
||||
reader, writer = await asyncio.open_unix_connection(str(socket_path))
|
||||
writer.write(b'unquiet')
|
||||
await writer.drain()
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
print(f"Unquiet command sent to bot {bot_id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error sending unquiet command to bot {bot_id}: {e}")
|
||||
return False
|
||||
|
||||
async def list_bots(self) -> bool:
|
||||
"""List all running bots.
|
||||
|
||||
@ -465,8 +535,8 @@ class BotManager:
|
||||
async def main():
|
||||
parser = argparse.ArgumentParser(description='Icecast IRC Bot Manager')
|
||||
parser.add_argument('--config', type=str, help='Path to config file or directory')
|
||||
parser.add_argument('command', choices=['start', 'stop', 'restart', 'list'], help='Command to execute')
|
||||
parser.add_argument('bot_id', nargs='?', help='Bot ID for start/stop/restart commands, or "all" to stop all bots')
|
||||
parser.add_argument('command', choices=['start', 'stop', 'restart', 'list', 'quiet', 'unquiet'], help='Command to execute')
|
||||
parser.add_argument('bot_id', nargs='?', help='Bot ID for start/stop/restart/quiet/unquiet commands, or "all" to stop all bots')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
@ -539,6 +609,28 @@ async def main():
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
elif args.command == 'quiet':
|
||||
should_cleanup = False # Don't need cleanup for quiet command
|
||||
if not args.bot_id:
|
||||
print("Error: bot_id required for quiet command")
|
||||
sys.exit(1)
|
||||
if args.bot_id == "all":
|
||||
print("Error: quiet all is not supported")
|
||||
sys.exit(1)
|
||||
if not await manager.quiet_bot(args.bot_id):
|
||||
sys.exit(1)
|
||||
|
||||
elif args.command == 'unquiet':
|
||||
should_cleanup = False # Don't need cleanup for unquiet command
|
||||
if not args.bot_id:
|
||||
print("Error: bot_id required for unquiet command")
|
||||
sys.exit(1)
|
||||
if args.bot_id == "all":
|
||||
print("Error: unquiet all is not supported")
|
||||
sys.exit(1)
|
||||
if not await manager.unquiet_bot(args.bot_id):
|
||||
sys.exit(1)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
should_cleanup = True # Need cleanup for keyboard interrupt
|
||||
except Exception:
|
||||
|
||||
@ -69,3 +69,5 @@ echo "icecast-irc-bot-manager list # List running bots"
|
||||
echo "icecast-irc-bot-manager start --config FILE # Start a bot"
|
||||
echo "icecast-irc-bot-manager stop BOT_ID # Stop a bot"
|
||||
echo "icecast-irc-bot-manager restart BOT_ID # Restart a bot"
|
||||
echo "icecast-irc-bot-manager quiet BOT_ID # Disable song announcements"
|
||||
echo "icecast-irc-bot-manager unquiet BOT_ID # Enable song announcements"
|
||||
86
main.py
86
main.py
@ -90,6 +90,8 @@ class RestartManager:
|
||||
self.socket_path = Path(tempfile.gettempdir()) / f"icecast_bot_{bot_id}.sock"
|
||||
self.server = None
|
||||
self.should_restart = False
|
||||
self.quiet_requested = False
|
||||
self.unquiet_requested = False
|
||||
|
||||
async def start(self):
|
||||
"""Start the restart manager server."""
|
||||
@ -119,6 +121,12 @@ class RestartManager:
|
||||
if data == b'restart':
|
||||
self.logger.info("Received restart request")
|
||||
self.should_restart = True
|
||||
elif data == b'quiet':
|
||||
self.logger.info("Received quiet request")
|
||||
self.quiet_requested = True
|
||||
elif data == b'unquiet':
|
||||
self.logger.info("Received unquiet request")
|
||||
self.unquiet_requested = True
|
||||
writer.close()
|
||||
await writer.wait_closed()
|
||||
except Exception as e:
|
||||
@ -256,6 +264,9 @@ class IcecastBot:
|
||||
self.monitor_task = None
|
||||
self.should_monitor = True
|
||||
self.is_monitoring = False
|
||||
self.should_announce = not self.config.get('announce', {}).get('quiet_on_start', False) # Respect quiet_on_start config
|
||||
if not self.should_announce:
|
||||
self.logger.info("Starting in quiet mode (announcements disabled) due to configuration")
|
||||
self.admin_users = self.config.get('admin', {}).get('users', ['*'])
|
||||
self.current_process = None
|
||||
self.should_exit = False
|
||||
@ -634,6 +645,40 @@ class IcecastBot:
|
||||
await message.recipient.message("Stream monitoring started.")
|
||||
self.command_handlers['start_monitoring'] = start_monitoring
|
||||
|
||||
@self.bot.on_message(create_command_pattern('quiet'))
|
||||
@self.admin_required
|
||||
async def quiet_bot(message):
|
||||
"""!quiet: Disable song announcements (admin only)
|
||||
|
||||
Continues monitoring the stream for metadata changes,
|
||||
but stops announcing songs in the channel.
|
||||
|
||||
Args:
|
||||
message: IRC message object
|
||||
"""
|
||||
self.should_announce = False
|
||||
self.logger.info("Song announcements disabled by admin command")
|
||||
if hasattr(message.recipient, 'name') and message.recipient.name.startswith('#'):
|
||||
await message.recipient.message("Song announcements disabled. Bot will continue monitoring but remain quiet.")
|
||||
self.command_handlers['quiet_bot'] = quiet_bot
|
||||
|
||||
@self.bot.on_message(create_command_pattern('unquiet'))
|
||||
@self.admin_required
|
||||
async def unquiet_bot(message):
|
||||
"""!unquiet: Enable song announcements (admin only)
|
||||
|
||||
Resumes announcing songs in the channel.
|
||||
The bot must already be monitoring the stream.
|
||||
|
||||
Args:
|
||||
message: IRC message object
|
||||
"""
|
||||
self.should_announce = True
|
||||
self.logger.info("Song announcements enabled by admin command")
|
||||
if hasattr(message.recipient, 'name') and message.recipient.name.startswith('#'):
|
||||
await message.recipient.message("Song announcements enabled. Bot will now announce songs.")
|
||||
self.command_handlers['unquiet_bot'] = unquiet_bot
|
||||
|
||||
def _build_command_mappings(self):
|
||||
"""Build bidirectional mappings between command patterns and method names.
|
||||
|
||||
@ -1018,9 +1063,14 @@ class IcecastBot:
|
||||
- The channel object is valid
|
||||
- The song doesn't match any ignore patterns
|
||||
- The announcement format is configured
|
||||
- The should_announce flag is True
|
||||
"""
|
||||
try:
|
||||
self.logger.info(f"Attempting to announce song: {song}")
|
||||
if not self.should_announce:
|
||||
self.logger.info(f"Song announcements are disabled, not announcing: {song}")
|
||||
return
|
||||
|
||||
if self.channel and self.should_announce_song(song):
|
||||
self.logger.debug(f"Song passed filters, preparing to announce")
|
||||
# Use the stored channel object directly
|
||||
@ -1257,9 +1307,45 @@ async def run_single_bot(bot: IcecastBot):
|
||||
bot.logger.info(f"Running single bot with ID: {bot.bot_id}")
|
||||
try:
|
||||
bot.logger.info("Starting bot...")
|
||||
|
||||
# Start a background task to check for quiet/unquiet requests
|
||||
async def check_quiet_unquiet():
|
||||
while True:
|
||||
try:
|
||||
# Check for quiet request
|
||||
if bot.restart_manager.quiet_requested:
|
||||
bot.logger.info("Processing quiet request")
|
||||
bot.should_announce = False
|
||||
bot.restart_manager.quiet_requested = False
|
||||
if bot.channel and hasattr(bot.channel, 'name') and bot.channel.name.startswith('#'):
|
||||
await bot.channel.message("Song announcements disabled via terminal command.")
|
||||
|
||||
# Check for unquiet request
|
||||
if bot.restart_manager.unquiet_requested:
|
||||
bot.logger.info("Processing unquiet request")
|
||||
bot.should_announce = True
|
||||
bot.restart_manager.unquiet_requested = False
|
||||
if bot.channel and hasattr(bot.channel, 'name') and bot.channel.name.startswith('#'):
|
||||
await bot.channel.message("Song announcements enabled via terminal command.")
|
||||
except Exception as e:
|
||||
bot.logger.error(f"Error in quiet/unquiet check: {e}")
|
||||
|
||||
await asyncio.sleep(1) # Check every second
|
||||
|
||||
# Start the background task
|
||||
quiet_check_task = asyncio.create_task(check_quiet_unquiet())
|
||||
|
||||
# Start the bot
|
||||
await bot.start()
|
||||
bot.logger.info("Bot start completed")
|
||||
|
||||
# Cancel the quiet check task
|
||||
quiet_check_task.cancel()
|
||||
try:
|
||||
await quiet_check_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
# Check if we should restart this bot
|
||||
if bot.restart_manager.should_restart:
|
||||
bot.logger.info("Bot should restart")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user