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()