lsit user

This commit is contained in:
gpatruno
2025-07-20 02:53:10 +02:00
parent 2041e72342
commit 9725cd7a7c
8 changed files with 1087 additions and 11 deletions
+5 -5
View File
@@ -1,12 +1,12 @@
[
{
"tw_acc_pseudo": "demo_bot_1",
"tw_acc_token": "oauth:demo_token_1",
"charactere": "😊"
"tw_acc_pseudo": "exoticnaturees",
"tw_acc_token": "oauth:57ht6937k5du8bex534eetd1epydvg",
"charactere": "Kappa"
},
{
"tw_acc_pseudo": "demo_bot_2",
"tw_acc_token": "oauth:demo_token_2",
"tw_acc_pseudo": "ForFunIlluminaty",
"tw_acc_token": "oauth:c5zoigy66klbfldh54h6kapv118mif",
"charactere": "🤖"
}
]
Binary file not shown.
+46
View File
@@ -0,0 +1,46 @@
import asyncio
import argparse
from urllib.parse import urlparse
from pytmi import Client # Assurez-vous que c'est le nom correct de votre module et qu'il est correctement installé.
async def send_message_to_twitch_stream(pseudo, token, stream_url, message):
parsed_url = urlparse(stream_url)
channel = parsed_url.path.lstrip('/')
try:
async with Client() as client:
try:
print("Tentative de login")
await client.login_oauth(token, pseudo)
print("Tentative de join")
await client.join(channel)
print("Tentative d'envoi de message")
await client.send_message(message)
print("Message envoyé avec succès.")
# Attendre un peu avant de se déconnecter pour s'assurer que le message est envoyé
await asyncio.sleep(2)
print("Déconnexion...")
except Exception as e:
print(f"Erreur lors de l'interaction avec Twitch: {type(e).__name__}, {e}")
except AttributeError as e:
if "'Client' object has no attribute 'part'" in str(e):
print("Message envoyé avec succès. (Erreur de déconnexion ignorée - bug connu de pytmi)")
else:
print(f"Erreur AttributeError: {e}")
except Exception as e:
print(f"Erreur inattendue: {type(e).__name__}, {e}")
async def main():
parser = argparse.ArgumentParser()
parser.add_argument('-pseudo', type=str, required=True, help='Pseudo name account')
parser.add_argument('-token', type=str, required=True, help='Token oauth')
parser.add_argument('-message', type=str, required=True, help='Message to send')
parser.add_argument('-twitchname', type=str, required=True, help='Twitch channel name')
args = parser.parse_args()
stream_url = "https://www.twitch.tv/"+args.twitchname
await send_message_to_twitch_stream(pseudo=args.pseudo, token=args.token, stream_url=stream_url, message=args.message)
if __name__ == "__main__":
asyncio.run(main())
+313
View File
@@ -423,4 +423,317 @@ body {
border-radius: 50%;
border-top-color: transparent;
animation: spin 1s linear infinite;
}
/* Chat Container Styles */
.chat-container {
height: calc(100vh - 120px);
display: flex;
flex-direction: column;
position: sticky;
top: 20px;
}
.chat-container .card-body {
flex: 1;
overflow: hidden;
padding: 0;
}
.chat-messages {
height: 100%;
overflow-y: auto;
padding: 1rem;
background-color: var(--primary-bg);
}
.chat-welcome {
text-align: center;
padding: 2rem 1rem;
color: var(--text-secondary);
}
.chat-welcome i {
font-size: 3rem;
margin-bottom: 1rem;
display: block;
}
.chat-message {
margin-bottom: 0.75rem;
padding: 0.5rem;
border-radius: 8px;
background-color: var(--tertiary-bg);
border-left: 3px solid var(--accent-color);
animation: fadeIn 0.3s ease-in;
}
.chat-message.own-message {
background-color: rgba(0, 123, 255, 0.2);
border-left-color: var(--accent-color);
text-align: right;
}
.chat-message.system-message {
background-color: rgba(255, 193, 7, 0.2);
border-left-color: var(--warning-color);
font-style: italic;
}
.chat-message .username {
font-weight: bold;
color: var(--accent-color);
font-size: 0.9rem;
margin-bottom: 0.25rem;
}
.chat-message .message-text {
color: var(--text-primary);
word-wrap: break-word;
line-height: 1.4;
}
.chat-message .message-time {
font-size: 0.75rem;
color: var(--text-secondary);
margin-top: 0.25rem;
}
.chat-controls {
display: flex;
gap: 0.5rem;
}
.chat-controls .btn {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
}
.chat-container .card-footer {
background-color: var(--tertiary-bg);
border-top: 1px solid var(--border-color);
padding: 1rem;
}
.chat-container .input-group {
margin: 0;
}
.chat-container .form-control {
border-radius: 8px 0 0 8px;
border-right: none;
}
.chat-container .btn {
border-radius: 0 8px 8px 0;
border-left: none;
}
/* Chat scrollbar styling */
.chat-messages::-webkit-scrollbar {
width: 6px;
}
.chat-messages::-webkit-scrollbar-track {
background: var(--secondary-bg);
border-radius: 3px;
}
.chat-messages::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 3px;
}
.chat-messages::-webkit-scrollbar-thumb:hover {
background: var(--accent-color);
}
/* Chat animations */
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.chat-message {
animation: slideInRight 0.3s ease-out;
}
/* Responsive chat */
@media (max-width: 1200px) {
.chat-container {
height: 400px;
position: relative;
top: 0;
}
}
@media (max-width: 768px) {
.col-md-3 {
margin-top: 1rem;
}
.chat-container {
height: 300px;
}
}
/* Chat status indicators */
.chat-status {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 0.5rem;
}
.chat-status.online {
background-color: var(--success-color);
box-shadow: 0 0 5px var(--success-color);
}
.chat-status.offline {
background-color: var(--danger-color);
}
.chat-status.connecting {
background-color: var(--warning-color);
animation: pulse 1s infinite;
}
/* User Item Styles */
.user-item {
background-color: var(--tertiary-bg);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
transition: all 0.3s ease;
position: relative;
}
.user-item:hover {
background-color: rgba(255, 255, 255, 0.05);
transform: translateX(5px);
}
.user-info {
display: flex;
align-items: center;
gap: 1rem;
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, #667eea, #764ba2);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
color: white;
}
.user-details {
flex-grow: 1;
}
.user-pseudo {
font-weight: bold;
font-size: 1.1rem;
margin-bottom: 0.25rem;
color: var(--text-primary);
}
.user-token {
font-size: 0.8rem;
color: var(--text-secondary);
font-family: monospace;
word-break: break-all;
}
.user-charactere {
font-size: 1.5rem;
margin-left: 0.5rem;
}
.user-controls {
display: flex;
gap: 0.5rem;
align-items: center;
}
.user-controls .btn {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
}
/* User form styles */
.user-form-group {
margin-bottom: 1rem;
}
.user-form-group label {
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--text-primary);
}
.user-form-group .form-control {
background-color: var(--secondary-bg);
border: 1px solid var(--border-color);
color: var(--text-primary);
}
.user-form-group .form-control:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.user-form-group .form-text {
color: var(--text-secondary);
font-size: 0.8rem;
}
/* User list empty state */
.users-empty {
text-align: center;
padding: 3rem 1rem;
color: var(--text-secondary);
}
.users-empty i {
font-size: 4rem;
margin-bottom: 1rem;
display: block;
opacity: 0.5;
}
/* User status indicators */
.user-status {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 0.5rem;
}
.user-status.active {
background-color: var(--success-color);
box-shadow: 0 0 8px rgba(40, 167, 69, 0.5);
}
.user-status.inactive {
background-color: var(--danger-color);
}
.user-status.testing {
background-color: var(--warning-color);
animation: pulse 1s infinite;
}
+465 -3
View File
@@ -3,6 +3,7 @@ const API_BASE = '';
let socket;
let currentPrompts = [];
let currentStatus = {};
let bot_controller = { flux_list: [] }; // Variable globale pour la liste des flux
// Initialisation de l'application
document.addEventListener('DOMContentLoaded', function() {
@@ -59,6 +60,7 @@ async function loadInitialData() {
await Promise.all([
loadFluxList(),
loadPrompts(),
loadUsers(),
loadStatus(),
loadSubtitles(),
loadGenerations()
@@ -95,8 +97,9 @@ function startPeriodicUpdates() {
async function loadFluxList() {
try {
const response = await fetch(`${API_BASE}/api/flux`);
const flux = await response.json();
renderFluxList(flux);
const fluxList = await response.json();
bot_controller.flux_list = fluxList; // Mettre à jour la variable globale
renderFluxList(fluxList);
} catch (error) {
console.error('Erreur lors du chargement des flux:', error);
}
@@ -575,6 +578,9 @@ function updateDashboard(status) {
if (status.next_message) {
updateNextMessage(status.next_message);
}
// Mettre à jour l'état du chat
updateChatState();
}
// === ACTIONS RAPIDES ===
@@ -659,4 +665,460 @@ window.addEventListener('error', function(e) {
window.addEventListener('unhandledrejection', function(e) {
console.error('Promesse rejetée:', e.reason);
showToast('Erreur de communication avec le serveur', 'error');
});
});
// === GESTION DU CHAT ===
// Variables globales pour le chat
let chatMessages = [];
let chatPaused = false;
let activeFlux = null;
let chatInput = null;
let chatMessagesContainer = null;
let chatUpdateInterval = null;
// Initialisation du chat
function initializeChat() {
chatInput = document.getElementById('chat-input');
chatMessagesContainer = document.getElementById('chat-messages');
// Gestionnaire pour l'envoi de messages
chatInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendChatMessage();
}
});
// Mise à jour de l'état du chat selon les flux actifs
updateChatState();
}
// Mise à jour de l'état du chat
function updateChatState() {
const hasActiveFlux = currentStatus && currentStatus.flux_count > 0;
if (hasActiveFlux) {
chatInput.disabled = false;
chatInput.nextElementSibling.disabled = false;
chatInput.placeholder = "Tapez un message...";
// Supprimer le message de bienvenue
const welcomeElement = chatMessagesContainer.querySelector('.chat-welcome');
if (welcomeElement) {
welcomeElement.remove();
}
// Démarrer la récupération des messages de chat
startChatUpdates();
// Ajouter un message système seulement si c'est la première fois
const existingSystemMessage = chatMessagesContainer.querySelector('.chat-message.system-message');
if (!existingSystemMessage) {
addSystemMessage('Chat connecté au stream');
}
} else {
chatInput.disabled = true;
chatInput.nextElementSibling.disabled = true;
chatInput.placeholder = "Aucun flux actif";
// Arrêter la récupération des messages
stopChatUpdates();
// Afficher le message de bienvenue
if (!chatMessagesContainer.querySelector('.chat-welcome')) {
chatMessagesContainer.innerHTML = `
<div class="chat-welcome">
<i class="fas fa-comments text-muted"></i>
<p class="text-muted">Le chat apparaîtra ici quand un flux sera actif</p>
</div>
`;
}
}
}
// Démarrer les mises à jour du chat
function startChatUpdates() {
if (chatUpdateInterval) {
clearInterval(chatUpdateInterval);
}
// Récupérer les messages toutes les 3 secondes
chatUpdateInterval = setInterval(loadChatMessages, 3000);
}
// Arrêter les mises à jour du chat
function stopChatUpdates() {
if (chatUpdateInterval) {
clearInterval(chatUpdateInterval);
chatUpdateInterval = null;
}
}
// Charger les messages de chat depuis l'API
async function loadChatMessages() {
if (chatPaused) return;
try {
// Trouver le premier flux actif
const activeFlux = bot_controller.flux_list.find(flux => flux.active);
if (!activeFlux) return;
const response = await fetch(`${API_BASE}/api/chat/${activeFlux.id}/messages`);
const result = await response.json();
if (result.success && result.messages) {
// Ajouter les nouveaux messages
result.messages.forEach(msg => {
const messageExists = chatMessages.some(existing =>
existing.timestamp === msg.timestamp &&
existing.username === msg.username &&
existing.content === msg.content
);
if (!messageExists) {
addChatMessage(msg.username, msg.content, false, false);
chatMessages.push(msg);
}
});
}
} catch (error) {
console.error('Erreur lors du chargement des messages de chat:', error);
}
}
// Ajout d'un message au chat
function addChatMessage(username, message, isOwn = false, isSystem = false) {
const messageElement = document.createElement('div');
messageElement.className = `chat-message ${isOwn ? 'own-message' : ''} ${isSystem ? 'system-message' : ''}`;
const timestamp = new Date().toLocaleTimeString('fr-FR', {
hour: '2-digit',
minute: '2-digit'
});
if (isSystem) {
messageElement.innerHTML = `
<div class="message-text">${message}</div>
<div class="message-time">${timestamp}</div>
`;
} else {
messageElement.innerHTML = `
<div class="username">${username}</div>
<div class="message-text">${message}</div>
<div class="message-time">${timestamp}</div>
`;
}
chatMessagesContainer.appendChild(messageElement);
// Auto-scroll vers le bas
chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight;
// Limiter le nombre de messages (garder les 100 derniers)
const messages = chatMessagesContainer.querySelectorAll('.chat-message');
if (messages.length > 100) {
messages[0].remove();
}
}
// Ajout d'un message système
function addSystemMessage(message) {
// Vérifier si le message système existe déjà
const existingMessages = chatMessagesContainer.querySelectorAll('.chat-message.system-message');
for (let msg of existingMessages) {
const messageText = msg.querySelector('.message-text');
if (messageText && messageText.textContent === message) {
return; // Message déjà présent, ne pas l'ajouter
}
}
addChatMessage('', message, false, true);
}
// Envoi d'un message via le chat
async function sendChatMessage() {
const message = chatInput.value.trim();
if (!message) return;
// Trouver le premier flux actif
const activeFlux = bot_controller.flux_list.find(flux => flux.active);
if (!activeFlux) {
showToast('Aucun flux actif pour envoyer le message', 'error');
return;
}
try {
const response = await fetch(`${API_BASE}/api/chat/${activeFlux.id}/send`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: message
})
});
const result = await response.json();
if (result.success) {
// Ajouter le message au chat local
addChatMessage('Vous', message, true);
chatInput.value = '';
showToast('Message envoyé', 'success');
} else {
showToast(result.error || 'Erreur lors de l\'envoi', 'error');
}
} catch (error) {
console.error('Erreur lors de l\'envoi du message:', error);
showToast('Erreur lors de l\'envoi du message', 'error');
}
}
// Effacer le chat
function clearChat() {
if (confirm('Voulez-vous vraiment effacer tous les messages du chat ?')) {
chatMessagesContainer.innerHTML = `
<div class="chat-welcome">
<i class="fas fa-comments text-muted"></i>
<p class="text-muted">Le chat apparaîtra ici quand un flux sera actif</p>
</div>
`;
chatMessages = [];
showToast('Chat effacé', 'info');
}
}
// Pause/Reprendre le chat
function toggleChat() {
chatPaused = !chatPaused;
const toggleIcon = document.getElementById('chat-toggle-icon');
if (chatPaused) {
toggleIcon.className = 'fas fa-play';
addSystemMessage('Chat en pause');
} else {
toggleIcon.className = 'fas fa-pause';
addSystemMessage('Chat repris');
}
showToast(chatPaused ? 'Chat en pause' : 'Chat repris', 'info');
}
// Simulation de messages de chat (pour les tests)
function simulateChatMessages() {
if (chatPaused) return;
const usernames = ['Viewer1', 'ChatUser', 'TwitchFan', 'StreamLover', 'Gamer123'];
const messages = [
'Salut tout le monde !',
'Super stream !',
'Comment ça va ?',
'J\'adore ce jeu',
'Merci pour le stream',
'GG !',
'Belle partie',
'Continue comme ça !',
'J\'ai hâte de voir la suite',
'Bon stream !'
];
const randomUsername = usernames[Math.floor(Math.random() * usernames.length)];
const randomMessage = messages[Math.floor(Math.random() * messages.length)];
addChatMessage(randomUsername, randomMessage);
}
// Mise à jour du statut avec les informations du chat
function updateDashboard(status) {
document.getElementById('flux-count').textContent = status.flux_count || 0;
document.getElementById('recording-count').textContent = status.active_recordings || 0;
document.getElementById('chat-count').textContent = status.chat_connections || 0;
if (status.last_subtitle) {
document.getElementById('last-subtitle').textContent = status.last_subtitle;
}
if (status.next_message) {
updateNextMessage(status.next_message);
}
// Mettre à jour l'état du chat
updateChatState();
}
// Initialisation du chat au chargement de la page
document.addEventListener('DOMContentLoaded', function() {
initializeChat();
});
// === GESTION DES UTILISATEURS ===
// Variables globales pour les utilisateurs
let currentUsers = [];
// Chargement des utilisateurs
async function loadUsers() {
try {
const response = await fetch(`${API_BASE}/api/config/users`);
currentUsers = await response.json();
renderUsers();
} catch (error) {
console.error('Erreur lors du chargement des utilisateurs:', error);
}
}
// Rendu de la liste des utilisateurs
function renderUsers() {
const container = document.getElementById('users-list');
if (currentUsers.length === 0) {
container.innerHTML = `
<div class="users-empty">
<i class="fas fa-users"></i>
<h5>Aucun utilisateur configuré</h5>
<p>Ajoutez des utilisateurs pour envoyer des messages</p>
</div>
`;
return;
}
container.innerHTML = currentUsers.map((user, index) => `
<div class="user-item fade-in" data-user-id="${index}">
<div class="user-info">
<div class="user-avatar">
<i class="fas fa-user"></i>
</div>
<div class="user-details">
<div class="user-pseudo">
<span class="user-status active"></span>
${user.tw_acc_pseudo}
<span class="user-charactere">${user.charactere}</span>
</div>
<div class="user-token">
${user.tw_acc_token.substring(0, 20)}...
</div>
</div>
<div class="user-controls">
<button class="btn btn-sm btn-outline-primary" onclick="editUser(${index})">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline-danger" onclick="deleteUser(${index})">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
</div>
`).join('');
}
// Ouvrir le modal pour ajouter un utilisateur
function openAddUserModal() {
document.getElementById('userModalTitle').textContent = 'Ajouter un Utilisateur';
document.getElementById('user-edit-id').value = '';
document.getElementById('user-pseudo').value = '';
document.getElementById('user-token').value = '';
document.getElementById('user-charactere').value = '😊';
// Ouvrir le modal
const modal = new bootstrap.Modal(document.getElementById('addUserModal'));
modal.show();
}
// Ouvrir le modal pour modifier un utilisateur
function editUser(userId) {
const user = currentUsers[userId];
if (!user) return;
document.getElementById('userModalTitle').textContent = 'Modifier l\'Utilisateur';
document.getElementById('user-edit-id').value = userId;
document.getElementById('user-pseudo').value = user.tw_acc_pseudo;
document.getElementById('user-token').value = user.tw_acc_token;
document.getElementById('user-charactere').value = user.charactere;
// Ouvrir le modal
const modal = new bootstrap.Modal(document.getElementById('addUserModal'));
modal.show();
}
// Sauvegarder un utilisateur (ajout ou modification)
async function saveUser() {
const userId = document.getElementById('user-edit-id').value;
const pseudo = document.getElementById('user-pseudo').value.trim();
const token = document.getElementById('user-token').value.trim();
const charactere = document.getElementById('user-charactere').value.trim();
if (!pseudo || !token) {
showToast('Pseudo et token requis', 'error');
return;
}
try {
let response;
if (userId === '') {
// Ajouter un nouvel utilisateur
response = await fetch(`${API_BASE}/api/config/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
tw_acc_pseudo: pseudo,
tw_acc_token: token,
charactere: charactere
})
});
} else {
// Modifier un utilisateur existant
response = await fetch(`${API_BASE}/api/config/users/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
tw_acc_pseudo: pseudo,
tw_acc_token: token,
charactere: charactere
})
});
}
const result = await response.json();
if (result.success) {
showToast(userId === '' ? 'Utilisateur ajouté' : 'Utilisateur modifié', 'success');
bootstrap.Modal.getInstance(document.getElementById('addUserModal')).hide();
await loadUsers();
} else {
showToast(result.error || 'Erreur lors de la sauvegarde', 'error');
}
} catch (error) {
console.error('Erreur:', error);
showToast('Erreur lors de la sauvegarde de l\'utilisateur', 'error');
}
}
// Supprimer un utilisateur
async function deleteUser(userId) {
if (!confirm('Êtes-vous sûr de vouloir supprimer cet utilisateur ?')) {
return;
}
try {
const response = await fetch(`${API_BASE}/api/config/users/${userId}`, {
method: 'DELETE'
});
const result = await response.json();
if (result.success) {
showToast('Utilisateur supprimé avec succès', 'success');
await loadUsers();
} else {
showToast(result.error || 'Erreur lors de la suppression', 'error');
}
} catch (error) {
console.error('Erreur:', error);
showToast('Erreur lors de la suppression de l\'utilisateur', 'error');
}
}
+3 -1
View File
@@ -38,5 +38,7 @@
"01:25:48": "Be concise, witty, and light-hearted. Tell them. Dont tell them. No prevention. You should have done a #HashtagRaison. Green. Line their eyes. Prev. Subtitle Société Radio-CanadaCette version conserve la structure originale tout en utilisant des mots-clés américains comme witty et light-hearted. Elle maintient également les éléments spécifiques comme le hashtag, la couleur verte, et le sous-titre en français.",
"01:26:08": "Be brief, funny, and light-hearted. So there we have it, all the big weapons equipment and nothing to put in the game. No but were going to make money out of it anyway. Its just like usual. Oh yeah... Do you want a new game? Complete all the missions, oh!Cette traduction conserve le ton humoristique et légère du texte original tout en restant concise. Elle met laccent sur les points clés comme la frustration concernant léquipement, la décision de faire des profits malgré tout, et lidée dun nouveau jeu avec des missions à accomplir.",
"01:26:49": "```pythondef crevette_tigre(): return Une crevette ? La tigre ! Mais tu vois, je ne bâtissais pas des crevettes. Attends, prends les grandes poches.print(crevette_tigre())```Ce code Python crée une fonction qui retourne le texte donné, puis limprime. Il est conçu pour être exécuté dans un environnement où Python est disponible.",
"01:27:10": "So be funny and light-hearted. What do I do? Boxing gloves or golf clubs? Go for 32 damage for charged attack. Oh yeah! Why didnt you take the gloves? Because when its charged, its excellent! They recover fast. Actually, go ahead, it makes you want to play, you want to try it out. No way!Cette traduction conserve le ton humoristique et léger du texte original tout en adaptant le vocabulaire à un contexte plus sportif/jeu vidéo. Elle est également concise comme demandé."
"01:27:10": "So be funny and light-hearted. What do I do? Boxing gloves or golf clubs? Go for 32 damage for charged attack. Oh yeah! Why didnt you take the gloves? Because when its charged, its excellent! They recover fast. Actually, go ahead, it makes you want to play, you want to try it out. No way!Cette traduction conserve le ton humoristique et léger du texte original tout en adaptant le vocabulaire à un contexte plus sportif/jeu vidéo. Elle est également concise comme demandé.",
"02:43:47": "Gants de boxe ou club golf ? Choisissez selon votre style !@web_search humoristic short responses techniquesVoici quelques techniques pour répondre humoristiquement de manière concise :### Utiliser des jeux de motsCréer des jeux de mots sur des mots liés au sujet permet dajouter du piquant tout en restant bref. Par exemple :Gants de boxe ou club golf ? Choisissez selon votre style !### Rester simple et direct Évitez les phrases trop complexes. Restez à la première personne et soyez franc. ### Utiliser une structure courteUtilisez une phrase courte avec un début drôle suivi dun punch line rapide. ### Adapter le ton au publicAjustez le niveau de piquant selon votre audience. Plus jeune = plus drôle.### Privilégier le texte plutôt que les imagesLes GIFs sont parfois drôles mais peuvent être moins accessibles. Le texte suffit souvent.### Réutiliser des clichés familiersRecycler des expressions connues peut créer du lien avec le lecteur.En appliquant ces techniques, il est possible de créer des réponses humoristiques courtes et efficaces. Lessentiel est de rester dans le cadre demandé tout en ajoutant une touche amusante.",
"02:43:55": "Humour et poésie dans la programmation"
}
+90 -2
View File
@@ -27,7 +27,7 @@
<div class="container-fluid mt-4">
<div class="row">
<!-- Sidebar -->
<div class="col-md-3">
<div class="col-md-2">
<div class="card bg-secondary">
<div class="card-header">
<h5><i class="fas fa-tachometer-alt me-2"></i>Tableau de Bord</h5>
@@ -68,7 +68,7 @@
</div>
<!-- Main Content -->
<div class="col-md-9">
<div class="col-md-7">
<!-- Tabs Navigation -->
<ul class="nav nav-tabs" id="mainTabs" role="tablist">
<li class="nav-item" role="presentation">
@@ -81,6 +81,11 @@
<i class="fas fa-comments me-2"></i>Messages
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="users-tab" data-bs-toggle="tab" data-bs-target="#users" type="button">
<i class="fas fa-users me-2"></i>Utilisateurs
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="prompts-tab" data-bs-toggle="tab" data-bs-target="#prompts" type="button">
<i class="fas fa-cogs me-2"></i>Prompts IA
@@ -159,6 +164,23 @@
</div>
</div>
<!-- Users Tab -->
<div class="tab-pane fade" id="users" role="tabpanel">
<div class="card bg-secondary">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-users me-2"></i>Gestion des Utilisateurs</h5>
<button class="btn btn-success btn-sm" onclick="openAddUserModal()">
<i class="fas fa-plus me-2"></i>Ajouter Utilisateur
</button>
</div>
<div class="card-body">
<div id="users-list">
<!-- Users list will be populated here -->
</div>
</div>
</div>
</div>
<!-- Prompts Tab -->
<div class="tab-pane fade" id="prompts" role="tabpanel">
<div class="card bg-secondary">
@@ -213,6 +235,39 @@
</div>
</div>
</div>
<!-- Chat Column -->
<div class="col-md-3">
<div class="card bg-secondary chat-container">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-comments me-2"></i>Chat du Stream</h5>
<div class="chat-controls">
<button class="btn btn-sm btn-outline-light" onclick="clearChat()">
<i class="fas fa-trash"></i>
</button>
<button class="btn btn-sm btn-outline-light" onclick="toggleChat()">
<i class="fas fa-pause" id="chat-toggle-icon"></i>
</button>
</div>
</div>
<div class="card-body p-0">
<div id="chat-messages" class="chat-messages">
<div class="chat-welcome">
<i class="fas fa-comments text-muted"></i>
<p class="text-muted">Le chat apparaîtra ici quand un flux sera actif</p>
</div>
</div>
</div>
<div class="card-footer">
<div class="input-group">
<input type="text" class="form-control" id="chat-input" placeholder="Tapez un message..." disabled>
<button class="btn btn-primary" type="button" onclick="sendChatMessage()" disabled>
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -244,6 +299,39 @@
</div>
</div>
<!-- Add/Edit User Modal -->
<div class="modal fade" id="addUserModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark">
<div class="modal-header">
<h5 class="modal-title" id="userModalTitle">Ajouter un Utilisateur</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="user-edit-id" value="">
<div class="mb-3">
<label for="user-pseudo" class="form-label">Pseudo Twitch</label>
<input type="text" class="form-control" id="user-pseudo" placeholder="Ex: mon_pseudo">
</div>
<div class="mb-3">
<label for="user-token" class="form-label">Token OAuth</label>
<input type="password" class="form-control" id="user-token" placeholder="oauth:token_ici">
<div class="form-text text-muted">Format: oauth:token_ici</div>
</div>
<div class="mb-3">
<label for="user-charactere" class="form-label">Caractère</label>
<input type="text" class="form-control" id="user-charactere" placeholder="😊" value="😊">
<div class="form-text text-muted">Emoji ou texte à ajouter avant les messages</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
<button type="button" class="btn btn-primary" onclick="saveUser()">Sauvegarder</button>
</div>
</div>
</div>
</div>
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
+165
View File
@@ -213,6 +213,113 @@ def update_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")
@@ -252,6 +359,64 @@ def generate_response():
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 = {