Ajout de boutons de modération dans la shoutbox pour les modérateurs Twitch, permettant de supprimer des messages, de mettre en timeout ou de bannir des utilisateurs directement depuis l'interface. Mise à jour de la logique de journalisation pour inclure le nom de l'administrateur au lieu de "WebApp". Amélioration de l'expérience utilisateur avec des prompts pour la durée et la raison des actions de modération.
This commit is contained in:
@@ -676,10 +676,23 @@ function addMessageToDisplay(username, message, timestamp, badges, userColor) {
|
|||||||
if (badges && badges.is_vip) badgeIcons += '<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-pink-100 text-pink-800 dark:bg-pink-900/30 dark:text-pink-400" title="VIP">VIP</span>';
|
if (badges && badges.is_vip) badgeIcons += '<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-pink-100 text-pink-800 dark:bg-pink-900/30 dark:text-pink-400" title="VIP">VIP</span>';
|
||||||
if (badges && badges.is_subscriber) badgeIcons += '<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-400" title="Abonné">SUB</span>';
|
if (badges && badges.is_subscriber) badgeIcons += '<span class="inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-400" title="Abonné">SUB</span>';
|
||||||
|
|
||||||
|
var safeUser = escapeHtml(username);
|
||||||
|
var modButtons = '<div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity ml-auto flex-shrink-0">'
|
||||||
|
+ '<button onclick="executeModerationAction(\'clean\', { username: \'' + safeUser + '\' })" class="p-1 rounded hover:bg-red-100 dark:hover:bg-red-900/30 text-gray-400 hover:text-red-500 transition-colors" title="Supprimer les messages">'
|
||||||
|
+ '<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/></svg>'
|
||||||
|
+ '</button>'
|
||||||
|
+ '<button onclick="promptTimeout(\'' + safeUser + '\')" class="p-1 rounded hover:bg-yellow-100 dark:hover:bg-yellow-900/30 text-gray-400 hover:text-yellow-500 transition-colors" title="Timeout">'
|
||||||
|
+ '<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>'
|
||||||
|
+ '</button>'
|
||||||
|
+ '<button onclick="promptBan(\'' + safeUser + '\')" class="p-1 rounded hover:bg-red-100 dark:hover:bg-red-900/30 text-gray-400 hover:text-red-600 transition-colors" title="Ban">'
|
||||||
|
+ '<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"/></svg>'
|
||||||
|
+ '</button>'
|
||||||
|
+ '</div>';
|
||||||
|
|
||||||
var messageDiv = document.createElement('div');
|
var messageDiv = document.createElement('div');
|
||||||
messageDiv.className = 'bg-white dark:bg-gray-800 rounded-lg p-3 text-sm animate-fade-in border-l-2';
|
messageDiv.className = 'group bg-white dark:bg-gray-800 rounded-lg p-3 text-sm animate-fade-in border-l-2';
|
||||||
messageDiv.style.borderLeftColor = userColor || '#9146FF';
|
messageDiv.style.borderLeftColor = userColor || '#9146FF';
|
||||||
messageDiv.innerHTML = '<div class="flex items-start gap-2"><div class="flex-1 min-w-0"><div class="flex items-center gap-2 mb-1 flex-wrap"><span class="font-bold" style="color: ' + (userColor || '#9146FF') + '">' + escapeHtml(username) + '</span>' + badgeIcons + '<span class="text-xs text-gray-500 dark:text-gray-400">' + timeStr + '</span></div><div class="text-gray-900 dark:text-gray-100 break-words">' + escapeHtml(message) + '</div></div></div>';
|
messageDiv.innerHTML = '<div class="flex items-start gap-2"><div class="flex-1 min-w-0"><div class="flex items-center gap-2 mb-1 flex-wrap"><span class="font-bold" style="color: ' + (userColor || '#9146FF') + '">' + safeUser + '</span>' + badgeIcons + '<span class="text-xs text-gray-500 dark:text-gray-400">' + timeStr + '</span>' + modButtons + '</div><div class="text-gray-900 dark:text-gray-100 break-words">' + escapeHtml(message) + '</div></div></div>';
|
||||||
|
|
||||||
messagesContainer.appendChild(messageDiv);
|
messagesContainer.appendChild(messageDiv);
|
||||||
while (messagesContainer.children.length > 100) messagesContainer.removeChild(messagesContainer.firstChild);
|
while (messagesContainer.children.length > 100) messagesContainer.removeChild(messagesContainer.firstChild);
|
||||||
@@ -806,6 +819,26 @@ function executeQuickAction(action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function promptTimeout(username) {
|
||||||
|
var duration = prompt('Durée du timeout en secondes pour ' + username + ' :', '600');
|
||||||
|
if (duration !== null) {
|
||||||
|
duration = parseInt(duration);
|
||||||
|
if (duration > 0) {
|
||||||
|
var reason = prompt('Raison (optionnel) :', 'Timeout via chat') || 'Timeout via chat';
|
||||||
|
executeModerationAction('timeout', { username: username, duration: duration, reason: reason });
|
||||||
|
} else {
|
||||||
|
showNotification('Durée invalide', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function promptBan(username) {
|
||||||
|
if (confirm('Êtes-vous sûr de vouloir ban ' + username + ' ?')) {
|
||||||
|
var reason = prompt('Raison (optionnel) :', 'Ban via chat') || 'Ban via chat';
|
||||||
|
executeModerationAction('ban', { username: username, reason: reason });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function executeModerationAction(action, params) {
|
function executeModerationAction(action, params) {
|
||||||
fetch('{{ url_for("execute_moderation_action") }}', {
|
fetch('{{ url_for("execute_moderation_action") }}', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -393,6 +393,8 @@ def execute_moderation_action():
|
|||||||
from twitchAPI.chat import ChatMessage
|
from twitchAPI.chat import ChatMessage
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
admin_name = f"WebApp ({current_user.username})"
|
||||||
|
|
||||||
# Exécuter l'action de manière asynchrone
|
# Exécuter l'action de manière asynchrone
|
||||||
try:
|
try:
|
||||||
async def execute_action():
|
async def execute_action():
|
||||||
@@ -409,7 +411,7 @@ def execute_moderation_action():
|
|||||||
|
|
||||||
if user_id:
|
if user_id:
|
||||||
await twitchBot.twitch.ban_user(broadcaster_id, moderator_id, user_id, reason=reason, duration=duration)
|
await twitchBot.twitch.ban_user(broadcaster_id, moderator_id, user_id, reason=reason, duration=duration)
|
||||||
_log_action("timeout", "WebApp", username, f"{duration}s - {reason}")
|
_log_action("timeout", admin_name, username, f"{duration}s - {reason}")
|
||||||
return {"success": True, "message": f"Timeout de {username} pour {duration}s"}
|
return {"success": True, "message": f"Timeout de {username} pour {duration}s"}
|
||||||
return {"success": False, "error": f"Utilisateur {username} introuvable"}
|
return {"success": False, "error": f"Utilisateur {username} introuvable"}
|
||||||
|
|
||||||
@@ -424,7 +426,7 @@ def execute_moderation_action():
|
|||||||
|
|
||||||
if user_id:
|
if user_id:
|
||||||
await twitchBot.twitch.ban_user(broadcaster_id, moderator_id, user_id, reason=reason)
|
await twitchBot.twitch.ban_user(broadcaster_id, moderator_id, user_id, reason=reason)
|
||||||
_log_action("ban", "WebApp", username, reason)
|
_log_action("ban", admin_name, username, reason)
|
||||||
return {"success": True, "message": f"Ban de {username}"}
|
return {"success": True, "message": f"Ban de {username}"}
|
||||||
return {"success": False, "error": f"Utilisateur {username} introuvable"}
|
return {"success": False, "error": f"Utilisateur {username} introuvable"}
|
||||||
|
|
||||||
@@ -439,12 +441,12 @@ def execute_moderation_action():
|
|||||||
user_id = await _get_user_id(twitchBot.twitch, username)
|
user_id = await _get_user_id(twitchBot.twitch, username)
|
||||||
if user_id:
|
if user_id:
|
||||||
await twitchBot.twitch.delete_chat_messages(broadcaster_id, moderator_id, user_id=user_id)
|
await twitchBot.twitch.delete_chat_messages(broadcaster_id, moderator_id, user_id=user_id)
|
||||||
_log_action("clean", "WebApp", username)
|
_log_action("clean", admin_name, username)
|
||||||
return {"success": True, "message": f"Messages de {username} supprimés"}
|
return {"success": True, "message": f"Messages de {username} supprimés"}
|
||||||
return {"success": False, "error": f"Utilisateur {username} introuvable"}
|
return {"success": False, "error": f"Utilisateur {username} introuvable"}
|
||||||
else:
|
else:
|
||||||
await twitchBot.twitch.delete_chat_messages(broadcaster_id, moderator_id)
|
await twitchBot.twitch.delete_chat_messages(broadcaster_id, moderator_id)
|
||||||
_log_action("clean", "WebApp", None, "Chat complet")
|
_log_action("clean", admin_name, None, "Chat complet")
|
||||||
return {"success": True, "message": "Chat nettoyé"}
|
return {"success": True, "message": "Chat nettoyé"}
|
||||||
|
|
||||||
elif action == 'permit':
|
elif action == 'permit':
|
||||||
@@ -473,19 +475,19 @@ def execute_moderation_action():
|
|||||||
|
|
||||||
if action == 'subon':
|
if action == 'subon':
|
||||||
await twitchBot.twitch.update_chat_settings(broadcaster_id, moderator_id, subscriber_mode=True)
|
await twitchBot.twitch.update_chat_settings(broadcaster_id, moderator_id, subscriber_mode=True)
|
||||||
_log_action("subon", "WebApp")
|
_log_action("subon", admin_name)
|
||||||
return {"success": True, "message": "Mode abonnés activé"}
|
return {"success": True, "message": "Mode abonnés activé"}
|
||||||
elif action == 'suboff':
|
elif action == 'suboff':
|
||||||
await twitchBot.twitch.update_chat_settings(broadcaster_id, moderator_id, subscriber_mode=False)
|
await twitchBot.twitch.update_chat_settings(broadcaster_id, moderator_id, subscriber_mode=False)
|
||||||
_log_action("suboff", "WebApp")
|
_log_action("suboff", admin_name)
|
||||||
return {"success": True, "message": "Mode abonnés désactivé"}
|
return {"success": True, "message": "Mode abonnés désactivé"}
|
||||||
elif action == 'emoteon':
|
elif action == 'emoteon':
|
||||||
await twitchBot.twitch.update_chat_settings(broadcaster_id, moderator_id, emote_mode=True)
|
await twitchBot.twitch.update_chat_settings(broadcaster_id, moderator_id, emote_mode=True)
|
||||||
_log_action("emoteon", "WebApp")
|
_log_action("emoteon", admin_name)
|
||||||
return {"success": True, "message": "Mode emote activé"}
|
return {"success": True, "message": "Mode emote activé"}
|
||||||
elif action == 'emoteoff':
|
elif action == 'emoteoff':
|
||||||
await twitchBot.twitch.update_chat_settings(broadcaster_id, moderator_id, emote_mode=False)
|
await twitchBot.twitch.update_chat_settings(broadcaster_id, moderator_id, emote_mode=False)
|
||||||
_log_action("emoteoff", "WebApp")
|
_log_action("emoteoff", admin_name)
|
||||||
return {"success": True, "message": "Mode emote désactivé"}
|
return {"success": True, "message": "Mode emote désactivé"}
|
||||||
|
|
||||||
return {"success": False, "error": f"Action '{action}' non reconnue"}
|
return {"success": False, "error": f"Action '{action}' non reconnue"}
|
||||||
|
|||||||
Reference in New Issue
Block a user