mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-09-01 16:48:26 +00:00
added generic per-command channel configuration, allows commands to be mapped to different channels or dms only
This commit is contained in:
+35
-25
@@ -85,6 +85,29 @@ bot_latitude = 40.7128
|
||||
# Example: -74.0060 for New York City, -123.00 for Victoria BC
|
||||
bot_longitude = -74.0060
|
||||
|
||||
# Maximum number of channels to fetch from MeshCore node
|
||||
# MeshCore supports up to 40 channels (default: 40)
|
||||
# Set to a lower value if you want to limit channel fetching for performance
|
||||
max_channels = 12
|
||||
|
||||
# Interval-based advertising settings
|
||||
# Send periodic flood adverts at specified intervals
|
||||
# 0: Disabled (default)
|
||||
# >0: Send flood advert every N hours
|
||||
advert_interval_hours = 0
|
||||
|
||||
# Send startup advert when bot finishes initializing
|
||||
# false: No startup advert (default)
|
||||
# zero-hop: Send local broadcast advert
|
||||
# flood: Send network-wide flood advert
|
||||
startup_advert = false
|
||||
|
||||
# Auto-manage contact list when new contacts are discovered
|
||||
# device: Device handles auto-addition using standard auto-discovery mode, bot manages contact list capacity (purge old contacts when near limits)
|
||||
# bot: Bot automatically adds new companion contacts to device, bot manages contact list capacity (purge old contacts when near limits)
|
||||
# false: Manual mode - no automatic actions, use !repeater commands to manage contacts (default)
|
||||
auto_manage_contacts = false
|
||||
|
||||
[Localization]
|
||||
# Language code for bot responses (en, es, es-MX, es-ES, fr, de, ja, etc.)
|
||||
# Default: en (English)
|
||||
@@ -166,24 +189,6 @@ dadjoke_enabled = true
|
||||
# true: Split long jokes into multiple messages
|
||||
long_jokes = false
|
||||
|
||||
# Send startup advert when bot finishes initializing
|
||||
# false: No startup advert (default)
|
||||
# zero-hop: Send local broadcast advert
|
||||
# flood: Send network-wide flood advert
|
||||
startup_advert = false
|
||||
|
||||
# Auto-manage contact list when new contacts are discovered
|
||||
# device: Device handles auto-addition using standard auto-discovery mode, bot manages contact list capacity (purge old contacts when near limits)
|
||||
# bot: Bot automatically adds new companion contacts to device, bot manages contact list capacity (purge old contacts when near limits)
|
||||
# false: Manual mode - no automatic actions, use !repeater commands to manage contacts (default)
|
||||
auto_manage_contacts = false
|
||||
|
||||
# Interval-based advertising settings
|
||||
# Send periodic flood adverts at specified intervals
|
||||
# 0: Disabled (default)
|
||||
# >0: Send flood advert every N hours
|
||||
advert_interval_hours = 0
|
||||
|
||||
[Keywords]
|
||||
# Keyword-response pairs (keyword = response format)
|
||||
# Available fields: {sender}, {connection_info}, {snr}, {timestamp}, {path}, {path_distance}, {firstlast_distance}
|
||||
@@ -198,7 +203,7 @@ advert_interval_hours = 0
|
||||
test = "ack @[{sender}]{phrase_part} | {connection_info} | Received at: {timestamp}"
|
||||
ping = "Pong!"
|
||||
pong = "Ping!"
|
||||
help = "Bot Help: test (or t), ping, help, hello, cmd, advert, @string, wx, aqi, sun, moon, solar, hfcond, satpass, prefix, path, sports, dice, roll, stats | Use 'help <command>' for details"
|
||||
help = "Bot Help: test (or t), ping, help, hello, cmd, advert, @string, wx, aqi, sun, moon, solar, hfcond, satpass, prefix, path, sports, dice, roll, stats | More: 'help <command>'"
|
||||
cmd = "Available commands: test (or t), ping, help, hello, cmd, advert, @string, wx, aqi, sun, moon, solar, hfcond, satpass, prefix, path, sports, dice, roll, stats"
|
||||
|
||||
[Channels]
|
||||
@@ -256,10 +261,12 @@ meshcore_log_level = INFO
|
||||
enabled = false
|
||||
|
||||
# Channels where greetings should occur (comma-separated)
|
||||
# IMPORTANT: Leave commented out (or omit entirely) to use global monitor_channels (default behavior)
|
||||
# If uncommented with empty value (channels = ), command will be DM-only
|
||||
# Comma-separated list to restrict to specific channels (only greeter command works there)
|
||||
# Example: channels = general,welcome,newbies
|
||||
# If not specified, uses the channels from [Channels] monitor_channels setting
|
||||
# Example: general,welcome,newbies
|
||||
# Leave empty to use monitor_channels from [Channels] section
|
||||
channels =
|
||||
# channels =
|
||||
|
||||
# Greeting message template (default for all channels)
|
||||
# Available fields: {sender} - the user's name/ID
|
||||
@@ -484,9 +491,12 @@ sports_enabled = true
|
||||
# Comma-separated list of team names (use lowercase)
|
||||
teams = seahawks,mariners,sounders,kraken
|
||||
|
||||
# Channels where sports command is allowed (leave empty for all channels)
|
||||
# Comma-separated list of channel names
|
||||
channels = general,#bot,#sounders,#seahawks
|
||||
# Channels where sports command is allowed
|
||||
# IMPORTANT: Leave commented out (or omit entirely) to use global monitor_channels (default behavior)
|
||||
# If uncommented with empty value (channels = ), command will be DM-only
|
||||
# Comma-separated list to restrict to specific channels (only sports command works there)
|
||||
# Example: channels = #sounders,#seahawks (only sports command works in these channels)
|
||||
# channels = general,#bot,#sounders,#seahawks
|
||||
|
||||
# Channel overrides for sports command
|
||||
# Format: channel_name = default_team
|
||||
|
||||
@@ -1,239 +0,0 @@
|
||||
# Modern MeshCore Bot Data Viewer v2.0
|
||||
|
||||
## 🚀 Complete Replacement Using Flask-SocketIO 5.x Best Practices
|
||||
|
||||
This is a **complete ground-up rewrite** of the MeshCore Bot Data Viewer, designed to eliminate all the recurring issues with the original implementation.
|
||||
|
||||
## 🔍 Why a Complete Rewrite?
|
||||
|
||||
The original web viewer had **fundamental architectural issues**:
|
||||
|
||||
- ❌ **Fighting Against Flask-SocketIO Design**: Manual background threads with complex timeout handling
|
||||
- ❌ **Resource Leaks**: 170+ file descriptors, connection leaks, memory issues
|
||||
- ❌ **Hanging Issues**: Background thread blocking, SocketIO emit timeouts
|
||||
- ❌ **Circuit Breaker Problems**: Too aggressive settings, no auto-recovery
|
||||
- ❌ **Poor Connection Management**: Manual tracking instead of built-in features
|
||||
|
||||
## ✅ Modern Solution Benefits
|
||||
|
||||
### **1. Event-Driven Architecture**
|
||||
- **No Background Threads**: Uses Flask-SocketIO's built-in event system
|
||||
- **Direct Broadcasting**: `socketio.emit()` instead of complex queue management
|
||||
- **Automatic Connection Management**: Built-in client tracking and cleanup
|
||||
|
||||
### **2. Flask-SocketIO 5.x Best Practices**
|
||||
- **Proper Configuration**: Modern ping/pong, timeouts, and logging
|
||||
- **Event Handlers**: Clean `@socketio.on()` decorators
|
||||
- **Built-in Error Handling**: Proper SocketIO error management
|
||||
|
||||
### **3. Performance Improvements**
|
||||
- **90% Reduction in File Descriptors**: 170+ → <20
|
||||
- **No More Hanging**: Event-driven prevents thread blocking
|
||||
- **Better Stability**: Built-in connection lifecycle management
|
||||
- **Lower Resource Usage**: No background threads or complex timeouts
|
||||
|
||||
## 📁 File Structure
|
||||
|
||||
```
|
||||
modules/web_viewer/
|
||||
├── modern_viewer.py # Main modern web viewer
|
||||
├── templates/
|
||||
│ ├── modern_base.html # Base template with navigation
|
||||
│ ├── modern_index.html # Dashboard
|
||||
│ ├── modern_realtime.html # Real-time monitoring
|
||||
│ ├── modern_contacts.html # Contact management
|
||||
│ ├── contacts.html # Contact tracking
|
||||
│ ├── modern_cache.html # Cache management
|
||||
│ ├── modern_purging.html # Purging log
|
||||
│ └── modern_stats.html # Statistics
|
||||
└── start_modern_viewer.sh # Startup script
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### **1. Start the Modern Web Viewer**
|
||||
```bash
|
||||
# Make executable and run
|
||||
chmod +x start_modern_viewer.sh
|
||||
./start_modern_viewer.sh
|
||||
```
|
||||
|
||||
### **2. Access the Web Interface**
|
||||
- **Dashboard**: http://127.0.0.1:8080/
|
||||
- **Real-time**: http://127.0.0.1:8080/realtime
|
||||
- **Contacts**: http://127.0.0.1:8080/contacts
|
||||
- **Tracking**: http://127.0.0.1:8080/tracking
|
||||
- **Cache**: http://127.0.0.1:8080/cache
|
||||
- **Purging**: http://127.0.0.1:8080/purging
|
||||
- **Stats**: http://127.0.0.1:8080/stats
|
||||
|
||||
### **3. API Endpoints**
|
||||
- **Health Check**: `GET /api/health`
|
||||
- **Statistics**: `GET /api/stats`
|
||||
- **Contacts**: `GET /api/contacts`
|
||||
- **Tracking**: `GET /api/tracking`
|
||||
- **Cache**: `GET /api/cache`
|
||||
- **Purging**: `GET /api/purging`
|
||||
- **Stream Data**: `POST /api/stream_data`
|
||||
|
||||
## 🔧 Features
|
||||
|
||||
### **Complete Feature Parity**
|
||||
- ✅ **Dashboard**: System overview and quick actions
|
||||
- ✅ **Real-time Monitoring**: Command and packet streaming
|
||||
- ✅ **Contact Management**: View, search, and manage contacts
|
||||
- ✅ **Contact Tracking**: Monitor contact activity and routing
|
||||
- ✅ **Cache Management**: Database cache and storage management
|
||||
- ✅ **Purging Log**: Data cleanup and maintenance logs
|
||||
- ✅ **Statistics**: Performance and usage analytics
|
||||
|
||||
### **Modern Improvements**
|
||||
- ✅ **Responsive Design**: Bootstrap 5.1.3 with modern UI
|
||||
- ✅ **Real-time Updates**: WebSocket connections with auto-reconnection
|
||||
- ✅ **Search & Filtering**: Advanced contact and data filtering
|
||||
- ✅ **Export Functionality**: CSV export for all data types
|
||||
- ✅ **Error Handling**: Comprehensive error management
|
||||
- ✅ **Performance Monitoring**: Built-in health checks and metrics
|
||||
|
||||
## 🛠️ Technical Architecture
|
||||
|
||||
### **Backend (Flask-SocketIO 5.x)**
|
||||
```python
|
||||
# Modern SocketIO Configuration
|
||||
self.socketio = SocketIO(
|
||||
self.app,
|
||||
cors_allowed_origins="*",
|
||||
max_http_buffer_size=1000000,
|
||||
ping_timeout=5, # Flask-SocketIO 5.x default
|
||||
ping_interval=25, # Flask-SocketIO 5.x default
|
||||
logger=True, # Proper logging
|
||||
engineio_logger=True, # EngineIO logging
|
||||
async_mode='threading' # Better stability
|
||||
)
|
||||
```
|
||||
|
||||
### **Frontend (Modern JavaScript)**
|
||||
```javascript
|
||||
// Modern Socket.IO client with proper error handling
|
||||
class ModernConnectionManager {
|
||||
constructor() {
|
||||
this.socket = io({
|
||||
transports: ['websocket', 'polling'],
|
||||
timeout: 5000,
|
||||
forceNew: true
|
||||
});
|
||||
|
||||
this.setupSocketEvents();
|
||||
this.startPingInterval();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Database Integration**
|
||||
- **Connection Pooling**: Thread-safe database connections
|
||||
- **Automatic Cleanup**: Connection timeout and refresh
|
||||
- **Error Handling**: Comprehensive database error management
|
||||
|
||||
## 📊 Performance Comparison
|
||||
|
||||
| Metric | Original | Modern v2.0 | Improvement |
|
||||
|--------|----------|-------------|-------------|
|
||||
| **File Descriptors** | 170+ | <20 | 90% reduction |
|
||||
| **Hanging Issues** | Every 40 minutes | None | 100% eliminated |
|
||||
| **Circuit Breaker Trips** | Frequent | Rare | 95% reduction |
|
||||
| **Code Complexity** | High | Low | 80% reduction |
|
||||
| **Maintainability** | Difficult | Easy | 90% improvement |
|
||||
|
||||
## 🔄 Migration Strategy
|
||||
|
||||
### **Phase 1: Parallel Deployment**
|
||||
1. Deploy modern implementation alongside current one
|
||||
2. Test with real bot data for 24+ hours
|
||||
3. Compare performance and stability metrics
|
||||
|
||||
### **Phase 2: Gradual Migration**
|
||||
1. Switch bot integration to use modern web viewer
|
||||
2. Monitor for extended periods (48+ hours)
|
||||
3. Verify all functionality works correctly
|
||||
|
||||
### **Phase 3: Full Replacement**
|
||||
1. Replace current web viewer with modern implementation
|
||||
2. Remove old code and dependencies
|
||||
3. Update documentation and deployment scripts
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### **Common Issues**
|
||||
|
||||
**1. Port Already in Use**
|
||||
```bash
|
||||
# Check what's using port 8080
|
||||
lsof -i :8080
|
||||
# Kill the process if needed
|
||||
kill -9 <PID>
|
||||
```
|
||||
|
||||
**2. Database Connection Issues**
|
||||
```bash
|
||||
# Check database file permissions
|
||||
ls -la meshcore_bot.db
|
||||
# Ensure the file is readable/writable
|
||||
```
|
||||
|
||||
**3. SocketIO Connection Issues**
|
||||
- Check browser console for errors
|
||||
- Verify WebSocket support in browser
|
||||
- Check firewall settings
|
||||
|
||||
### **Logs and Debugging**
|
||||
- **Web Viewer Logs**: `logs/web_viewer_modern.log`
|
||||
- **Bot Integration Logs**: `meshcore_bot.log`
|
||||
- **Health Check**: `GET /api/health`
|
||||
|
||||
## 🎯 Expected Results
|
||||
|
||||
### **Immediate Benefits**
|
||||
- ✅ **No More Hanging**: 40-minute hanging pattern eliminated
|
||||
- ✅ **No More Circuit Breaker Trips**: Robust connection handling
|
||||
- ✅ **No More Resource Leaks**: Proper connection lifecycle management
|
||||
- ✅ **Better Performance**: Lower resource usage, faster response times
|
||||
|
||||
### **Long-term Benefits**
|
||||
- ✅ **Easier Maintenance**: Clean, modern codebase
|
||||
- ✅ **Better Debugging**: Comprehensive logging and error handling
|
||||
- ✅ **Future-proof**: Following Flask-SocketIO best practices
|
||||
- ✅ **Scalable**: Event-driven architecture scales better
|
||||
|
||||
## 📈 Monitoring
|
||||
|
||||
### **Health Checks**
|
||||
- **System Status**: Dashboard shows real-time system health
|
||||
- **Connection Status**: WebSocket connection monitoring
|
||||
- **Database Health**: Automatic database connection management
|
||||
- **Performance Metrics**: Built-in performance monitoring
|
||||
|
||||
### **Key Metrics to Monitor**
|
||||
- **File Descriptors**: Should stay <20 (vs 170+ before)
|
||||
- **Connection Stability**: No hanging or timeouts
|
||||
- **Memory Usage**: Stable, no leaks
|
||||
- **Response Times**: Fast, consistent performance
|
||||
|
||||
## 🚀 Next Steps
|
||||
|
||||
1. **Test the Modern Implementation**: Run for 24+ hours to verify stability
|
||||
2. **Compare with Original**: Monitor resource usage and performance
|
||||
3. **Migrate Bot Integration**: Update bot to use modern web viewer
|
||||
4. **Full Deployment**: Replace original implementation completely
|
||||
|
||||
## 📞 Support
|
||||
|
||||
For issues or questions about the modern web viewer:
|
||||
|
||||
1. **Check Logs**: Review `logs/web_viewer_modern.log`
|
||||
2. **Health Check**: Visit `http://127.0.0.1:8080/api/health`
|
||||
3. **Restart**: Use `./start_modern_viewer.sh` to restart
|
||||
4. **Debug Mode**: Add `--debug` flag for detailed logging
|
||||
|
||||
---
|
||||
|
||||
**The modern web viewer represents a complete architectural improvement, eliminating all the fundamental issues with the original implementation while providing better performance, stability, and maintainability.**
|
||||
@@ -14,13 +14,13 @@ from meshcore import EventType
|
||||
class ChannelManager:
|
||||
"""Manages channel operations and information with enhanced concurrent fetching"""
|
||||
|
||||
def __init__(self, bot, max_channels: int = 8):
|
||||
def __init__(self, bot, max_channels: int = 40):
|
||||
"""
|
||||
Initialize the channel manager
|
||||
|
||||
Args:
|
||||
bot: The MeshCore bot instance
|
||||
max_channels: Maximum number of channels to fetch (default 8)
|
||||
max_channels: Maximum number of channels to fetch (default 40)
|
||||
"""
|
||||
self.bot = bot
|
||||
self.logger = bot.logger
|
||||
|
||||
@@ -26,6 +26,9 @@ class BaseCommand(ABC):
|
||||
self.bot = bot
|
||||
self.logger = bot.logger
|
||||
self._last_execution_time = 0
|
||||
|
||||
# Load allowed channels from config (standardized channel override)
|
||||
self.allowed_channels = self._load_allowed_channels()
|
||||
|
||||
# Load translated keywords after initialization
|
||||
self._load_translated_keywords()
|
||||
@@ -130,8 +133,65 @@ class BaseCommand(ABC):
|
||||
"""Get help text for this command"""
|
||||
return self.description or "No help available for this command."
|
||||
|
||||
def _load_allowed_channels(self) -> Optional[List[str]]:
|
||||
"""
|
||||
Load allowed channels from config.
|
||||
|
||||
Config format: [CommandName_Command]
|
||||
channels = channel1,channel2,channel3
|
||||
|
||||
Returns:
|
||||
- None: Use global monitor_channels (default behavior)
|
||||
- Empty list []: Command disabled for all channels (only DMs)
|
||||
- List of channels: Command only works in these channels
|
||||
"""
|
||||
# Derive section name from command name
|
||||
# Convert "sports" -> "Sports_Command", "greeter" -> "Greeter_Command", etc.
|
||||
section_name = f"{self.name.title().replace('_', '_')}_Command"
|
||||
|
||||
# Try to get channels config
|
||||
channels_str = self.get_config_value(section_name, 'channels', fallback=None, value_type='str')
|
||||
|
||||
if channels_str is None:
|
||||
return None # Use global monitor_channels
|
||||
|
||||
if channels_str.strip() == '':
|
||||
return [] # Disabled for all channels (DM only)
|
||||
|
||||
# Parse comma-separated list
|
||||
channels = [ch.strip() for ch in channels_str.split(',') if ch.strip()]
|
||||
return channels if channels else None
|
||||
|
||||
def is_channel_allowed(self, message: MeshMessage) -> bool:
|
||||
"""
|
||||
Check if this command is allowed in the message's channel.
|
||||
|
||||
Returns:
|
||||
- True if DM and command allows DMs (unless requires_dm is False, but that's separate)
|
||||
- True if channel is in allowed_channels (or None for global)
|
||||
- False otherwise
|
||||
"""
|
||||
# DMs are always allowed (unless requires_dm is False, but that's checked separately)
|
||||
if message.is_dm:
|
||||
return True
|
||||
|
||||
# If no channel override, use global monitor_channels
|
||||
if self.allowed_channels is None:
|
||||
return message.channel in self.bot.command_manager.monitor_channels
|
||||
|
||||
# If empty list, command is disabled for channels (DM only)
|
||||
if self.allowed_channels == []:
|
||||
return False
|
||||
|
||||
# Check if channel is in allowed list
|
||||
return message.channel in self.allowed_channels
|
||||
|
||||
def can_execute(self, message: MeshMessage) -> bool:
|
||||
"""Check if this command can be executed with the given message"""
|
||||
# Check channel access (standardized channel override)
|
||||
if not self.is_channel_allowed(message):
|
||||
return False
|
||||
|
||||
# Check if command requires DM and message is not DM
|
||||
if self.requires_dm and not message.is_dm:
|
||||
return False
|
||||
|
||||
@@ -74,7 +74,8 @@ class GreeterCommand(BaseCommand):
|
||||
if self.backfill_lookback_days == 0:
|
||||
self.backfill_lookback_days = None
|
||||
|
||||
# Load greeter-specific channels (if not set, uses monitor_channels from [Channels] section)
|
||||
# Note: allowed_channels is now loaded by BaseCommand from config
|
||||
# Keep greeter_channels for backward compatibility and case-insensitive matching
|
||||
channels_str = self.get_config_value('Greeter_Command', 'channels', fallback='')
|
||||
if channels_str:
|
||||
# Store both original and lowercase versions for case-insensitive matching
|
||||
@@ -766,16 +767,19 @@ class GreeterCommand(BaseCommand):
|
||||
if not message.channel:
|
||||
return False
|
||||
|
||||
# Check if channel is in greeter-specific channels or fall back to monitor_channels
|
||||
if self.greeter_channels is not None:
|
||||
# Use greeter-specific channels if configured (case-insensitive matching)
|
||||
if message.channel and message.channel.lower() not in self.greeter_channels_lower:
|
||||
return False
|
||||
else:
|
||||
# Fall back to general monitor_channels setting (case-insensitive matching)
|
||||
monitor_channels_lower = [ch.lower() for ch in self.bot.command_manager.monitor_channels]
|
||||
if message.channel and message.channel.lower() not in monitor_channels_lower:
|
||||
return False
|
||||
# Check channel access using standardized method (with case-insensitive fallback)
|
||||
# First try standardized method (case-sensitive)
|
||||
if not self.is_channel_allowed(message):
|
||||
# If standardized check fails, try case-insensitive matching for backward compatibility
|
||||
if self.greeter_channels is not None:
|
||||
# Use greeter-specific channels if configured (case-insensitive matching)
|
||||
if message.channel and message.channel.lower() not in self.greeter_channels_lower:
|
||||
return False
|
||||
else:
|
||||
# Fall back to general monitor_channels setting (case-insensitive matching)
|
||||
monitor_channels_lower = [ch.lower() for ch in self.bot.command_manager.monitor_channels]
|
||||
if message.channel and message.channel.lower() not in monitor_channels_lower:
|
||||
return False
|
||||
|
||||
# Check if we're in an active rollout period
|
||||
rollout_active = self._is_rollout_active()
|
||||
|
||||
@@ -425,6 +425,8 @@ class SportsCommand(BaseCommand):
|
||||
|
||||
# Load default teams from config
|
||||
self.default_teams = self.load_default_teams()
|
||||
# Note: allowed_channels is now loaded by BaseCommand from config
|
||||
# Keep sports_channels for backward compatibility (used in execute() for channel-specific team defaults)
|
||||
self.sports_channels = self.load_sports_channels()
|
||||
self.channel_overrides = self.load_channel_overrides()
|
||||
|
||||
@@ -518,16 +520,11 @@ class SportsCommand(BaseCommand):
|
||||
if not sports_enabled:
|
||||
return False
|
||||
|
||||
# Check if command requires DM and message is not DM
|
||||
if self.requires_dm and not message.is_dm:
|
||||
# Channel access is now handled by BaseCommand.is_channel_allowed()
|
||||
# Call parent can_execute() which includes channel checking
|
||||
if not super().can_execute(message):
|
||||
return False
|
||||
|
||||
# Check if command requires specific channels (only for channel messages, not DMs)
|
||||
if not message.is_dm and self.sports_channels and message.channel not in self.sports_channels:
|
||||
# Check if this channel has an override (allows sports command even if not in main channels list)
|
||||
if message.channel not in self.channel_overrides:
|
||||
return False
|
||||
|
||||
# Check per-user cooldown (don't set it here, just check)
|
||||
if self.cooldown_seconds > 0:
|
||||
import time
|
||||
|
||||
+23
-13
@@ -114,7 +114,11 @@ class MeshCoreBot:
|
||||
|
||||
self.message_handler = MessageHandler(self)
|
||||
self.command_manager = CommandManager(self)
|
||||
self.channel_manager = ChannelManager(self)
|
||||
|
||||
# Load max_channels from config (default 40, MeshCore supports up to 40 channels)
|
||||
max_channels = self.config.getint('Bot', 'max_channels', fallback=40)
|
||||
self.channel_manager = ChannelManager(self, max_channels=max_channels)
|
||||
|
||||
self.scheduler = MessageScheduler(self)
|
||||
|
||||
# Initialize repeater manager
|
||||
@@ -234,6 +238,24 @@ bot_latitude = 40.7128
|
||||
# Example: -74.0060 for New York City, -123.00 for Victoria BC
|
||||
bot_longitude = -74.0060
|
||||
|
||||
# Interval-based advertising settings
|
||||
# Send periodic flood adverts at specified intervals
|
||||
# 0: Disabled (default)
|
||||
# >0: Send flood advert every N hours
|
||||
advert_interval_hours = 0
|
||||
|
||||
# Send startup advert when bot finishes initializing
|
||||
# false: No startup advert (default)
|
||||
# zero-hop: Send local broadcast advert
|
||||
# flood: Send network-wide flood advert
|
||||
startup_advert = false
|
||||
|
||||
# Auto-manage contact list when new contacts are discovered
|
||||
# device: Device handles auto-addition using standard auto-discovery mode, bot manages contact list capacity (purge old contacts when near limits)
|
||||
# bot: Bot automatically adds new companion contacts to device, bot manages contact list capacity (purge old contacts when near limits)
|
||||
# false: Manual mode - no automatic actions, use !repeater commands to manage contacts (default)
|
||||
auto_manage_contacts = false
|
||||
|
||||
[Jokes]
|
||||
# Enable or disable the joke command
|
||||
# true: Joke command is available
|
||||
@@ -256,18 +278,6 @@ dadjoke_enabled = true
|
||||
# true: Split long jokes into multiple messages
|
||||
long_jokes = false
|
||||
|
||||
# Send startup advert when bot finishes initializing
|
||||
# false: No startup advert (default)
|
||||
# zero-hop: Send local broadcast advert
|
||||
# flood: Send network-wide flood advert
|
||||
startup_advert = false
|
||||
|
||||
# Auto-manage contact list when new contacts are discovered
|
||||
# device: Device handles auto-addition using standard auto-discovery mode, bot manages contact list capacity (purge old contacts when near limits)
|
||||
# bot: Bot automatically adds new companion contacts to device, bot manages contact list capacity (purge old contacts when near limits)
|
||||
# false: Manual mode - no automatic actions, use !repeater commands to manage contacts (default)
|
||||
auto_manage_contacts = false
|
||||
|
||||
[Admin_ACL]
|
||||
# Admin Access Control List (ACL) for restricted commands
|
||||
# Only users with public keys listed here can execute admin commands
|
||||
|
||||
@@ -1637,8 +1637,21 @@ class MessageHandler:
|
||||
self.logger.debug(f"Ignoring message from banned user: {message.sender_id}")
|
||||
return False
|
||||
|
||||
# Check if channel is monitored
|
||||
if not message.is_dm and message.channel and message.channel not in self.bot.command_manager.monitor_channels:
|
||||
# Check if channel is monitored (with command override support)
|
||||
if not message.is_dm and message.channel:
|
||||
# Check if channel is in global monitor_channels
|
||||
if message.channel in self.bot.command_manager.monitor_channels:
|
||||
return True # Global allow - all commands can work
|
||||
|
||||
# Check if ANY command allows this channel (for selective access)
|
||||
for command_name, command in self.bot.command_manager.commands.items():
|
||||
if hasattr(command, 'is_channel_allowed') and callable(command.is_channel_allowed):
|
||||
if command.is_channel_allowed(message):
|
||||
# At least one command allows this channel
|
||||
self.logger.debug(f"Channel {message.channel} allowed by command '{command_name}' override")
|
||||
return True
|
||||
|
||||
# Channel not in global list and no command allows it
|
||||
self.logger.debug(f"Channel {message.channel} not in monitored channels: {self.bot.command_manager.monitor_channels}")
|
||||
return False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user