""" Main application entry point for the Zulip Bot application. """ import os from flask import Flask, request, jsonify from app.config import load_config def create_app(config_name=None): """Create and configure the Flask application.""" app = Flask(__name__) # Load configuration config = load_config(config_name) app.config.from_object(config) # Set DEBUG mode for the app app.config['DEBUG'] = True # Override any environment flags to disable safety filters os.environ['GEMINI_NO_SAFETY'] = 'true' # Apply NumPy compatibility patch for ChromaDB from app.utils import patch_chromadb_numpy patch_chromadb_numpy() # Initialize database connections from app.db import init_db init_db(app) # Check if we're in the main process or a Flask reloader worker # When Flask reloads in debug mode, it sets an environment variable # We only want to start services in the main process to avoid duplication is_flask_reloader_process = os.environ.get('WERKZEUG_RUN_MAIN') == 'true' is_main_process = not os.environ.get('WERKZEUG_RUN_MAIN') # Only start services in the main process or if --no-reload is used # This prevents duplicate services when using Flask's debug mode should_start_services = is_flask_reloader_process or is_main_process # Initialize message sync service and bot service regardless of process # but only start them in the appropriate process from app.utils.sync_service import MessageSyncService sync_service = MessageSyncService(sync_interval=60) # Sync every 60 seconds # Store sync_service in app context so it can be accessed elsewhere app.sync_service = sync_service # Initialize Zulip bot service from app.utils.bot_service import ZulipBotService bot_service = ZulipBotService() # Store bot_service in app context so it can be accessed elsewhere app.bot_service = bot_service # Start the services in a better way (avoiding deprecated before_first_request) # But only if this is the main process or Flask reloader's main thread with app.app_context(): # Add logging to help diagnose any issues app.logger.info(f"App initialization, should_start_services={should_start_services}, " f"is_main_process={is_main_process}, is_flask_reloader_process={is_flask_reloader_process}") if should_start_services: # Start the sync service app.logger.info("Starting sync service...") sync_service.start() # Start the bot service and log the result app.logger.info("Starting Zulip bot service...") if bot_service.thread and bot_service.thread.is_alive(): app.logger.info("Bot service is already running, not starting again") else: bot_service.start() app.logger.info("Bot service started successfully") else: app.logger.info("Skipping service startup in Flask reloader process") # Register a shutdown function to stop the services @app.teardown_appcontext def stop_services(exception=None): if hasattr(app, 'sync_service'): app.sync_service.stop() if hasattr(app, 'bot_service'): app.bot_service.stop() # Register blueprints # This will be implemented later @app.route('/health') def health_check(): """Simple health check endpoint.""" return jsonify({'status': 'ok'}) @app.route('/sync/now') def trigger_sync(): """Trigger an immediate sync.""" if hasattr(app, 'sync_service'): app.sync_service.sync_now() return jsonify({'status': 'sync_triggered'}) return jsonify({'status': 'error', 'message': 'Sync service not available'}), 500 @app.route('/bot/status') def bot_status(): """Get the status of the bot service.""" if hasattr(app, 'bot_service') and app.bot_service.thread and app.bot_service.thread.is_alive(): return jsonify({'status': 'running'}) return jsonify({'status': 'stopped'}) @app.route('/bot/start', methods=['POST']) def start_bot(): """Start the bot service.""" if hasattr(app, 'bot_service'): app.bot_service.start() return jsonify({'status': 'started'}) return jsonify({'status': 'error', 'message': 'Bot service not available'}), 500 @app.route('/bot/stop', methods=['POST']) def stop_bot(): """Stop the bot service.""" if hasattr(app, 'bot_service'): app.bot_service.stop() return jsonify({'status': 'stopped'}) return jsonify({'status': 'error', 'message': 'Bot service not available'}), 500 @app.route('/bot/test', methods=['POST']) def test_bot(): """Send a test message to verify the bot is working.""" if not hasattr(app, 'bot_service'): return jsonify({'status': 'error', 'message': 'Bot service not available'}), 500 data = request.get_json() if not data or 'recipient' not in data or 'content' not in data: return jsonify({'status': 'error', 'message': 'Missing required fields: recipient, content'}), 400 result = app.bot_service.send_test_message(data['recipient'], data['content']) return jsonify({'status': 'sent', 'result': result}) @app.route('/bot/reset-cache', methods=['POST']) def reset_bot_cache(): """Reset the bot's message cache to fix issues with message processing.""" if not hasattr(app, 'bot_service'): return jsonify({'status': 'error', 'message': 'Bot service not available'}), 500 result = app.bot_service.reset_cache() return jsonify({'status': 'success', 'message': result}) return app