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# Netdata webhook challenge secret (required for webhook verification) 44SERVER_CHALLENGE_SECRET=your-challenge-secret-here 45 46# Optional: Override Zulip stream (default: netdata-alerts) 47# ZULIP_STREAM=custom-alerts-stream 48""" 49 50 with open(".env.sample", 'w') as f: 51 f.write(env_content) 52 53 # Sample zuliprc file 54 zuliprc_content = """[api] 55site=https://yourorg.zulipchat.com 56email=netdata-bot@yourorg.zulipchat.com 57key=your-api-key-here 58stream=netdata-alerts 59""" 60 61 with open(".zuliprc.sample", 'w') as f: 62 f.write(zuliprc_content) 63 64 print("Created sample configuration files:") 65 print(" - .env.sample") 66 print(" - .zuliprc.sample") 67 print() 68 print("Copy and customize these files:") 69 print(" cp .env.sample .env") 70 print(" cp .zuliprc.sample ~/.zuliprc") 71 72 73def main(): 74 """Main entry point.""" 75 parser = argparse.ArgumentParser( 76 description="Netdata Zulip Bot - Webhook service for Netdata Cloud notifications" 77 ) 78 parser.add_argument( 79 "--zuliprc", 80 help="Path to zuliprc configuration file (default: ~/.zuliprc)" 81 ) 82 parser.add_argument( 83 "--create-config", 84 action="store_true", 85 help="Create sample configuration files and exit" 86 ) 87 parser.add_argument( 88 "--env-config", 89 action="store_true", 90 help="Use environment variables for configuration instead of zuliprc" 91 ) 92 93 args = parser.parse_args() 94 95 setup_logging() 96 logger = structlog.get_logger() 97 98 if args.create_config: 99 create_sample_configs() 100 return 101 102 try: 103 # Load configuration 104 if args.env_config: 105 zulip_config, server_config = load_config() 106 # Validate that required Zulip fields are provided via environment 107 if not all([zulip_config.site, zulip_config.email, zulip_config.api_key]): 108 missing = [] 109 if not zulip_config.site: missing.append('ZULIP_SITE') 110 if not zulip_config.email: missing.append('ZULIP_EMAIL') 111 if not zulip_config.api_key: missing.append('ZULIP_API_KEY') 112 raise ValueError( 113 f"When using --env-config, these environment variables are required: {', '.join(missing)}" 114 ) 115 else: 116 zulip_config = load_zuliprc_config(args.zuliprc) 117 # Still need server config from environment 118 _, server_config = load_config() 119 120 # Create and start the webhook server 121 server = NetdataWebhookServer(zulip_config, server_config) 122 server.run() 123 124 except KeyboardInterrupt: 125 logger.info("Shutting down webhook server") 126 except Exception as e: 127 logger.error("Failed to start webhook server", error=str(e)) 128 sys.exit(1) 129 130 131if __name__ == "__main__": 132 main()