477 lines
16 KiB
Python
477 lines
16 KiB
Python
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/<int:flux_id>', 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/<int:flux_id>/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/<int:flux_id>/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/<int:user_id>', 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/<int:user_id>', 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/<int:flux_id>/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/<int:flux_id>/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) |