Netdata.cloud bot for Zulip
1"""Main entry point for the Netdata Zulip bot.""" 2 3import argparse 4import sys 5from pathlib import Path 6 7import structlog 8 9from .config import load_config, load_zuliprc_config 10from .models import ServerConfig 11from .server import NetdataWebhookServer 12 13 14def setup_logging(): 15 """Configure structured logging.""" 16 structlog.configure( 17 processors=[ 18 structlog.stdlib.filter_by_level, 19 structlog.stdlib.add_logger_name, 20 structlog.stdlib.add_log_level, 21 structlog.stdlib.PositionalArgumentsFormatter(), 22 structlog.processors.TimeStamper(fmt="iso"), 23 structlog.processors.StackInfoRenderer(), 24 structlog.processors.format_exc_info, 25 structlog.processors.UnicodeDecoder(), 26 structlog.processors.JSONRenderer() 27 ], 28 context_class=dict, 29 logger_factory=structlog.stdlib.LoggerFactory(), 30 wrapper_class=structlog.stdlib.BoundLogger, 31 cache_logger_on_first_use=True, 32 ) 33 34 35def create_sample_configs(): 36 """Create sample configuration files.""" 37 38 # Sample .env file 39 env_content = """# Server Configuration (HTTP only, TLS handled by reverse proxy) 40SERVER_HOST=0.0.0.0 41SERVER_PORT=8080 42 43# Optional: Override Zulip stream (default: netdata-alerts) 44# ZULIP_STREAM=custom-alerts-stream 45""" 46 47 with open(".env.sample", 'w') as f: 48 f.write(env_content) 49 50 # Sample zuliprc file 51 zuliprc_content = """[api] 52site=https://yourorg.zulipchat.com 53email=netdata-bot@yourorg.zulipchat.com 54key=your-api-key-here 55stream=netdata-alerts 56""" 57 58 with open(".zuliprc.sample", 'w') as f: 59 f.write(zuliprc_content) 60 61 print("Created sample configuration files:") 62 print(" - .env.sample") 63 print(" - .zuliprc.sample") 64 print() 65 print("Copy and customize these files:") 66 print(" cp .env.sample .env") 67 print(" cp .zuliprc.sample ~/.zuliprc") 68 69 70def main(): 71 """Main entry point.""" 72 parser = argparse.ArgumentParser( 73 description="Netdata Zulip Bot - Webhook service for Netdata Cloud notifications" 74 ) 75 parser.add_argument( 76 "--zuliprc", 77 help="Path to zuliprc configuration file (default: ~/.zuliprc)" 78 ) 79 parser.add_argument( 80 "--create-config", 81 action="store_true", 82 help="Create sample configuration files and exit" 83 ) 84 parser.add_argument( 85 "--env-config", 86 action="store_true", 87 help="Use environment variables for configuration instead of zuliprc" 88 ) 89 90 args = parser.parse_args() 91 92 setup_logging() 93 logger = structlog.get_logger() 94 95 if args.create_config: 96 create_sample_configs() 97 return 98 99 try: 100 # Load configuration 101 if args.env_config: 102 zulip_config, server_config = load_config() 103 # Validate that required Zulip fields are provided via environment 104 if not all([zulip_config.site, zulip_config.email, zulip_config.api_key]): 105 missing = [] 106 if not zulip_config.site: missing.append('ZULIP_SITE') 107 if not zulip_config.email: missing.append('ZULIP_EMAIL') 108 if not zulip_config.api_key: missing.append('ZULIP_API_KEY') 109 raise ValueError( 110 f"When using --env-config, these environment variables are required: {', '.join(missing)}" 111 ) 112 else: 113 zulip_config = load_zuliprc_config(args.zuliprc) 114 # Still need server config from environment 115 _, server_config = load_config() 116 117 # Create and start the webhook server 118 server = NetdataWebhookServer(zulip_config, server_config) 119 server.run() 120 121 except KeyboardInterrupt: 122 logger.info("Shutting down webhook server") 123 except Exception as e: 124 logger.error("Failed to start webhook server", error=str(e)) 125 sys.exit(1) 126 127 128if __name__ == "__main__": 129 main()