from flask import Flask, render_template, request, jsonify, redirect, url_for from flask_socketio import SocketIO, emit import json import os import threading import time from datetime import datetime import sys # Import des classes du bot sys.path.append('.') from fonction.first_class import RecordTwitch, Subtitle_translation, IA_generator, messageTwitch, TwitchChatBot, storage app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key-here' socketio = SocketIO(app, cors_allowed_origins="*") class BotController: def __init__(self): self.bots = {} # Stockage des instances de bots (pour l'utilisation interne) self.flux_list = [] # Liste des flux surveillés (pour l'API JSON) self.config = self.load_config() def load_config(self): try: with open('config/config.json', 'r') as file: return json.load(file) except FileNotFoundError: return {} def save_config(self): with open('config/config.json', 'w') as file: json.dump(self.config, file, indent=4, ensure_ascii=False) def add_flux(self, channel_name, record_audio=True): flux_id = len(self.flux_list) + 1 # Créer l'objet flux pour l'API (sans les instances de bots) flux_data = { 'id': flux_id, 'name': channel_name, 'twitchname': channel_name, 'record_audio': record_audio, 'active': True, 'created_at': datetime.now().isoformat(), 'status': 'starting' } try: # Créer le bot de chat pour ce flux chat_bot = TwitchChatBot(channel_name) self.bots[flux_id] = { 'chat_bot': chat_bot, 'record_bot': None } chat_bot.start_background() # Si enregistrement audio activé if record_audio: record_bot = RecordTwitch(channel_name, 60) self.bots[flux_id]['record_bot'] = record_bot threading.Thread(target=record_bot.main, daemon=True).start() # Mettre à jour le statut flux_data['status'] = 'active' self.flux_list.append(flux_data) return flux_id except Exception as e: print(f"Erreur lors de l'ajout du flux {channel_name}: {str(e)}") # Nettoyer en cas d'erreur if flux_id in self.bots: try: if self.bots[flux_id]['chat_bot']: self.bots[flux_id]['chat_bot'].stop() if self.bots[flux_id]['record_bot']: self.bots[flux_id]['record_bot'].stop() except: pass del self.bots[flux_id] flux_data['status'] = 'error' flux_data['error'] = str(e) self.flux_list.append(flux_data) raise e def remove_flux(self, flux_id): for i, flux in enumerate(self.flux_list): if flux['id'] == flux_id: # Arrêter les bots si ils existent if flux_id in self.bots: try: if self.bots[flux_id]['chat_bot']: self.bots[flux_id]['chat_bot'].stop() if self.bots[flux_id]['record_bot']: self.bots[flux_id]['record_bot'].stop() except Exception as e: print(f"Erreur lors de l'arrêt des bots: {e}") del self.bots[flux_id] del self.flux_list[i] return True return False def get_flux_list(self): # Retourner seulement les données JSON (pas les instances de bots) return self.flux_list bot_controller = BotController() @app.route('/') def index(): return render_template('index.html') @app.route('/api/flux', methods=['GET']) def get_flux(): return jsonify(bot_controller.get_flux_list()) @app.route('/api/flux', methods=['POST']) def add_flux(): data = request.json channel_name = data.get('channel_name') record_audio = data.get('record_audio', True) if not channel_name: return jsonify({'error': 'Nom du canal requis'}), 400 try: flux_id = bot_controller.add_flux(channel_name, record_audio) return jsonify({'success': True, 'flux_id': flux_id}) except Exception as e: error_msg = f"Erreur lors de l'ajout du flux: {str(e)}" print(f"Erreur API add_flux: {error_msg}") return jsonify({'error': error_msg}), 500 @app.route('/api/flux/', methods=['DELETE']) def remove_flux(flux_id): if bot_controller.remove_flux(flux_id): return jsonify({'success': True}) return jsonify({'error': 'Flux non trouvé'}), 404 @app.route('/api/flux//status', methods=['GET']) def get_flux_status(flux_id): """Obtenir le statut détaillé d'un flux spécifique""" for flux in bot_controller.flux_list: if flux['id'] == flux_id: status = { 'id': flux_id, 'name': flux['name'], 'active': flux['active'], 'status': flux.get('status', 'unknown'), 'created_at': flux['created_at'], 'bots': {} } # Ajouter les informations des bots si disponibles if flux_id in bot_controller.bots: bots = bot_controller.bots[flux_id] if bots['chat_bot']: status['bots']['chat'] = { 'running': bots['chat_bot'].is_running if hasattr(bots['chat_bot'], 'is_running') else True } if bots['record_bot']: status['bots']['record'] = { 'running': bots['record_bot'].running if hasattr(bots['record_bot'], 'running') else True } return jsonify(status) return jsonify({'error': 'Flux non trouvé'}), 404 @app.route('/api/flux//toggle', methods=['POST']) def toggle_flux(flux_id): """Activer/désactiver un flux""" for flux in bot_controller.flux_list: if flux['id'] == flux_id: flux['active'] = not flux['active'] # Arrêter/démarrer les bots selon le nouveau statut if flux_id in bot_controller.bots: bots = bot_controller.bots[flux_id] if not flux['active']: # Arrêter les bots try: if bots['chat_bot']: bots['chat_bot'].stop() if bots['record_bot']: bots['record_bot'].stop() except Exception as e: print(f"Erreur lors de l'arrêt des bots: {e}") else: # Redémarrer les bots try: if bots['chat_bot']: bots['chat_bot'].start_background() if bots['record_bot']: threading.Thread(target=bots['record_bot'].main, daemon=True).start() except Exception as e: print(f"Erreur lors du redémarrage des bots: {e}") return jsonify({'success': True, 'active': flux['active']}) return jsonify({'error': 'Flux non trouvé'}), 404 @app.route('/api/config/prompts', methods=['GET']) def get_prompts(): return jsonify(bot_controller.config.get('list_prompt', [])) @app.route('/api/config/prompts', methods=['POST']) def update_prompts(): data = request.json prompts = data.get('prompts', []) bot_controller.config['list_prompt'] = prompts bot_controller.save_config() return jsonify({'success': True}) @app.route('/api/config/users', methods=['GET']) def get_users(): """Récupérer la liste des utilisateurs""" try: with open('config/user.json', 'r') as file: users = json.load(file) return jsonify(users) except FileNotFoundError: return jsonify([]) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/config/users', methods=['POST']) def add_user(): """Ajouter un nouvel utilisateur""" data = request.json pseudo = data.get('tw_acc_pseudo') token = data.get('tw_acc_token') charactere = data.get('charactere', '😊') if not pseudo or not token: return jsonify({'error': 'Pseudo et token requis'}), 400 try: with open('config/user.json', 'r') as file: users = json.load(file) # Vérifier si l'utilisateur existe déjà for user in users: if user['tw_acc_pseudo'] == pseudo: return jsonify({'error': 'Cet utilisateur existe déjà'}), 400 # Ajouter le nouvel utilisateur new_user = { 'tw_acc_pseudo': pseudo, 'tw_acc_token': token, 'charactere': charactere } users.append(new_user) # Sauvegarder with open('config/user.json', 'w') as file: json.dump(users, file, indent=4, ensure_ascii=False) return jsonify({'success': True, 'user': new_user}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/config/users/', methods=['PUT']) def update_user(user_id): """Modifier un utilisateur existant""" data = request.json pseudo = data.get('tw_acc_pseudo') token = data.get('tw_acc_token') charactere = data.get('charactere', '😊') if not pseudo or not token: return jsonify({'error': 'Pseudo et token requis'}), 400 try: with open('config/user.json', 'r') as file: users = json.load(file) if user_id >= len(users): return jsonify({'error': 'Utilisateur non trouvé'}), 404 # Vérifier si le pseudo existe déjà (sauf pour l'utilisateur actuel) for i, user in enumerate(users): if i != user_id and user['tw_acc_pseudo'] == pseudo: return jsonify({'error': 'Ce pseudo est déjà utilisé'}), 400 # Mettre à jour l'utilisateur users[user_id] = { 'tw_acc_pseudo': pseudo, 'tw_acc_token': token, 'charactere': charactere } # Sauvegarder with open('config/user.json', 'w') as file: json.dump(users, file, indent=4, ensure_ascii=False) return jsonify({'success': True, 'user': users[user_id]}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/config/users/', methods=['DELETE']) def delete_user(user_id): """Supprimer un utilisateur""" try: with open('config/user.json', 'r') as file: users = json.load(file) if user_id >= len(users): return jsonify({'error': 'Utilisateur non trouvé'}), 404 # Supprimer l'utilisateur deleted_user = users.pop(user_id) # Sauvegarder with open('config/user.json', 'w') as file: json.dump(users, file, indent=4, ensure_ascii=False) return jsonify({'success': True, 'deleted_user': deleted_user}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/subtitles', methods=['GET']) def get_subtitles(): data = storage.read("subtitle_data") return jsonify(data) @app.route('/api/generations', methods=['GET']) def get_generations(): data = storage.read("IA_generator") return jsonify(data) @app.route('/api/send-message', methods=['POST']) def send_message(): data = request.json message = data.get('message') channel = data.get('channel', 'default') if not message: return jsonify({'error': 'Message requis'}), 400 # Trouver le bot de message pour ce canal try: msg_bot = messageTwitch("config/user.json", channel) msg_bot.send_message(message) return jsonify({'success': True}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/generate-response', methods=['POST']) def generate_response(): data = request.json text = data.get('text', '') try: ia_gen = IA_generator("config/config.json") ia_gen.main_ask(text) return jsonify({'success': True}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/chat//messages', methods=['GET']) def get_chat_messages(flux_id): """Récupérer les messages de chat d'un flux spécifique""" try: if flux_id not in bot_controller.bots: return jsonify({'error': 'Flux non trouvé'}), 404 chat_bot = bot_controller.bots[flux_id]['chat_bot'] if not chat_bot: return jsonify({'error': 'Bot de chat non disponible'}), 404 # Récupérer les messages du bot de chat messages = [] for msg in chat_bot.messages[-50:]: # Derniers 50 messages messages.append({ 'timestamp': msg.timestamp.isoformat(), 'username': msg.username, 'content': msg.content }) return jsonify({ 'success': True, 'flux_id': flux_id, 'messages': messages }) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/chat//send', methods=['POST']) def send_chat_message(flux_id): """Envoyer un message dans le chat d'un flux spécifique""" data = request.json message = data.get('message', '') if not message: return jsonify({'error': 'Message requis'}), 400 try: # Trouver le flux flux = None for f in bot_controller.flux_list: if f['id'] == flux_id: flux = f break if not flux: return jsonify({'error': 'Flux non trouvé'}), 404 # Envoyer le message msg_bot = messageTwitch("config/user.json", flux['twitchname']) msg_bot.send_message(message) return jsonify({'success': True}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/status', methods=['GET']) def get_status(): status = { 'flux_count': len(bot_controller.flux_list), 'active_recordings': sum(1 for f in bot_controller.flux_list if f['record_audio'] and f['active']), 'chat_connections': sum(1 for f in bot_controller.flux_list if f['active']), 'last_subtitle': '', 'next_message': '', 'recent_messages': [] } # Récupérer le dernier sous-titre subtitle_data = storage.read("subtitle_data") if subtitle_data: sorted_keys = sorted(subtitle_data.keys()) if sorted_keys: status['last_subtitle'] = subtitle_data[sorted_keys[-1]] # Récupérer la dernière génération generation_data = storage.read("IA_generator") if generation_data: sorted_keys = sorted(generation_data.keys()) if sorted_keys: status['next_message'] = generation_data[sorted_keys[-1]] return jsonify(status) @socketio.on('connect') def handle_connect(): print('Client connecté') emit('status', {'message': 'Connecté au serveur'}) @socketio.on('disconnect') def handle_disconnect(): print('Client déconnecté') # Thread pour envoyer les mises à jour en temps réel def background_updates(): while True: try: status = { 'timestamp': datetime.now().isoformat(), 'flux_count': len(bot_controller.flux_list), 'active_recordings': sum(1 for f in bot_controller.flux_list if f['record_audio'] and f['active']), } socketio.emit('status_update', status) time.sleep(5) # Mise à jour toutes les 5 secondes except Exception as e: print(f"Erreur dans background_updates: {e}") time.sleep(10) if __name__ == '__main__': # Démarrer les mises à jour en arrière-plan update_thread = threading.Thread(target=background_updates, daemon=True) update_thread.start() # Démarrer l'application Flask socketio.run(app, host='0.0.0.0', port=5000, debug=True)