Files
twitchBot-intelligent/twitch_bot/controller.py
T
2026-04-22 22:42:36 +02:00

346 lines
14 KiB
Python

"""Contrôle centralisé des flux Twitch, bots et boucles IA / envoi chat."""
import json
import threading
import time
from datetime import datetime
from fonction.first_class import (
IA_generator,
RecordTwitch,
Subtitle_translation,
TwitchChatBot,
messageTwitch,
storage,
)
from twitch_bot import chat_state
from twitch_bot.interaction_chat import InteractionChatProcessor
def _resolve_user_index(pseudo: str) -> int:
"""
Retourne l'index d'un compte dans config/user.json (case-insensitive).
Fallback: 0.
"""
try:
with open("config/user.json", "r", encoding="utf-8") as f:
users = json.load(f) or []
pseudo_l = (pseudo or "").strip().lstrip("@").lower()
for i, u in enumerate(users):
p = (u or {}).get("tw_acc_pseudo")
if isinstance(p, str) and p.strip().lower() == pseudo_l:
return i
except Exception:
pass
return 0
def _resolve_first_enabled_user_index() -> int:
try:
with open("config/user.json", "r", encoding="utf-8") as f:
users = json.load(f) or []
for i, u in enumerate(users):
if isinstance(u, dict) and u.get("enabled", True):
return i
except Exception:
pass
return 0
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()
self.ia_generator = None
self.control_twitch = None
self.ia_generator_running = False
self.control_twitch_running = False
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 get_system_status(self):
"""Obtenir le statut de tous les composants"""
return {
'ia_generator': {
'running': self.ia_generator_running,
'status': 'En cours' if self.ia_generator_running else 'Arrêté'
},
'control_twitch': {
'running': self.control_twitch_running,
'status': 'En cours' if self.control_twitch_running else 'Arrêté'
},
'flux_count': len(self.flux_list),
'active_flux': len([f for f in self.flux_list if f['active']])
}
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)
# Interaction chat (mentions -> réponses préenregistrées)
def get_registered_accounts():
try:
with open("config/user.json", "r", encoding="utf-8") as f:
users = json.load(f)
pseudos = []
for u in users or []:
p = (u or {}).get("tw_acc_pseudo")
enabled = (u or {}).get("enabled", True)
if enabled and isinstance(p, str) and p.strip():
pseudos.append(p.strip())
return pseudos
except Exception:
return []
def get_account_policies():
try:
with open("config/user.json", "r", encoding="utf-8") as f:
users = json.load(f) or []
out = {}
for u in users:
if not isinstance(u, dict):
continue
p = (u.get("tw_acc_pseudo") or "").strip().lstrip("@").lower()
if not p:
continue
out[p] = {
"enabled": bool(u.get("enabled", True)),
"interaction_bypass_antiloop": bool(u.get("interaction_bypass_antiloop", False)),
}
return out
except Exception:
return {}
msg_bot_for_interaction = messageTwitch("config/user.json", channel_name)
interaction = InteractionChatProcessor(
channel_name=channel_name,
get_registered_accounts=get_registered_accounts,
get_account_policies=get_account_policies,
send_message_as=lambda pseudo, text: msg_bot_for_interaction.send_message_user(
_resolve_user_index(pseudo), text
),
)
self.bots[flux_id] = {
'chat_bot': chat_bot,
'record_bot': None,
'subtitle_bot': None,
'ia_bot': None,
'message_bot': None,
'interaction': interaction,
}
chat_bot.start_background()
interaction.start_background(get_latest_messages=lambda: chat_bot.messages)
# 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()
# Démarrer le bot de sous-titres
subtitle_bot = Subtitle_translation("config/config.json")
self.bots[flux_id]['subtitle_bot'] = subtitle_bot
subtitle_bot.start_main_loop()
# Démarrer le générateur IA
ia_bot = IA_generator("config/config.json")
self.bots[flux_id]['ia_bot'] = ia_bot
ia_bot.start_main_loop()
# Démarrer le contrôleur de messages
message_bot = messageTwitch("config/user.json", channel_name)
self.bots[flux_id]['message_bot'] = message_bot
message_bot.start_loop_respond()
# 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()
if self.bots[flux_id]['subtitle_bot']:
self.bots[flux_id]['subtitle_bot'].stop()
if self.bots[flux_id]['ia_bot']:
self.bots[flux_id]['ia_bot'].stop()
if self.bots[flux_id]['message_bot']:
self.bots[flux_id]['message_bot'].stop()
except Exception:
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].get('interaction'):
self.bots[flux_id]['interaction'].stop()
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()
if self.bots[flux_id]['subtitle_bot']:
self.bots[flux_id]['subtitle_bot'].stop()
if self.bots[flux_id]['ia_bot']:
self.bots[flux_id]['ia_bot'].stop()
if self.bots[flux_id]['message_bot']:
self.bots[flux_id]['message_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
def start_ia_generator(self):
"""Démarrer le générateur IA de manière contrôlée"""
if self.ia_generator_running:
return False, "IA Generator déjà en cours d'exécution"
try:
self.ia_generator = IA_generator("config/config.json")
self.ia_generator_running = True
# Activer l'envoi de messages quand l'IA Generator est démarré
chat_state.chat_messages_enabled = True
# Démarrer dans un thread séparé
threading.Thread(target=self._ia_generator_loop, daemon=True).start()
print(f"[{datetime.now().strftime('%H:%M:%S')}] IA Generator démarré")
return True, "IA Generator démarré avec succès"
except Exception as e:
self.ia_generator_running = False
return False, f"Erreur lors du démarrage de l'IA Generator: {str(e)}"
def stop_ia_generator(self):
"""Arrêter le générateur IA"""
if not self.ia_generator_running:
return False, "IA Generator n'est pas en cours d'exécution"
try:
self.ia_generator_running = False
if self.ia_generator:
self.ia_generator.stop()
# Désactiver l'envoi de messages quand l'IA Generator est arrêté
chat_state.chat_messages_enabled = False
print(f"[{datetime.now().strftime('%H:%M:%S')}] IA Generator arrêté")
return True, "IA Generator arrêté avec succès"
except Exception as e:
return False, f"Erreur lors de l'arrêt de l'IA Generator: {str(e)}"
def _ia_generator_loop(self):
"""Boucle contrôlée pour l'IA Generator"""
while self.ia_generator_running:
try:
if self.ia_generator:
self.ia_generator.main_ask("") # Génération automatique
time.sleep(20) # Attendre 20 secondes entre les générations
except Exception as e:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur dans IA Generator: {e}")
time.sleep(10)
def start_control_twitch(self):
"""Démarrer le contrôleur Twitch de manière contrôlée"""
if self.control_twitch_running:
return False, "Control Twitch déjà en cours d'exécution"
try:
# Utiliser le premier utilisateur par défaut
self.control_twitch = messageTwitch("config/user.json", "default")
self.control_twitch_running = True
# Démarrer dans un thread séparé
threading.Thread(target=self._control_twitch_loop, daemon=True).start()
print(f"[{datetime.now().strftime('%H:%M:%S')}] Control Twitch démarré")
return True, "Control Twitch démarré avec succès"
except Exception as e:
self.control_twitch_running = False
return False, f"Erreur lors du démarrage de Control Twitch: {str(e)}"
def stop_control_twitch(self):
"""Arrêter le contrôleur Twitch"""
if not self.control_twitch_running:
return False, "Control Twitch n'est pas en cours d'exécution"
try:
self.control_twitch_running = False
if self.control_twitch:
self.control_twitch.stop()
print(f"[{datetime.now().strftime('%H:%M:%S')}] Control Twitch arrêté")
return True, "Control Twitch arrêté avec succès"
except Exception as e:
return False, f"Erreur lors de l'arrêt de Control Twitch: {str(e)}"
def _control_twitch_loop(self):
"""Boucle contrôlée pour Control Twitch"""
while self.control_twitch_running:
try:
if self.control_twitch:
# Vérifier s'il y a des générations à envoyer
generation_data = storage.read("IA_generator")
if generation_data:
sorted_keys = sorted(generation_data.keys())
if sorted_keys:
last_generation = generation_data[sorted_keys[-1]]
# Envoyer le message avec le premier utilisateur activé
self.control_twitch.send_message_user(_resolve_first_enabled_user_index(), last_generation)
# Supprimer la génération envoyée
storage.delete("IA_generator", sorted_keys[-1])
print(f"[{datetime.now().strftime('%H:%M:%S')}] Message envoyé: {last_generation[:50]}...")
time.sleep(10) # Attendre 10 secondes entre les vérifications
except Exception as e:
print(f"[{datetime.now().strftime('%H:%M:%S')}] Erreur dans Control Twitch: {e}")
time.sleep(10)
bot_controller = BotController()