Implementado Ban History, Restore Character, Restore Items

This commit is contained in:
adevopg
2025-02-19 22:12:22 +01:00
parent 137dc853f8
commit 3f962f8368
5 changed files with 437 additions and 131 deletions
+1
View File
@@ -282,6 +282,7 @@ class LoginAttempt(models.Model):
return f"{self.username} - {self.status} - {self.timestamp}"
class StripeLog(models.Model):
account_id = models.IntegerField() # ID de la cuenta en acore_auth
username = models.CharField(max_length=100) # Nombre de la cuenta
+92 -42
View File
@@ -1,45 +1,95 @@
<div class="main-page">
<div class="middle-content">
<div class="body-content">
<div class="title-content"><h1>Historial de sanciones</h1></div>
<div class="box-content">
<div class="title-box-content"><h2>Información</h2><a class="back-to-account" href="my-account">Regresar a mi cuenta</a><a class="back-to-account-responsive" href="my-account">Regresar</a></div>
<div class="body-box-content justified">
<br>
<p>Aquí podrás ver todas las sanciones que ha tenido la cuenta.</p>
<p>Si tienes una sanción vigente, verás que su estado indica <span class="red-info2">Activo</span>.</p>
<p>Para evitar que tu cuenta sea sancionada, recuerda respetar las normas de nuestro servidor.</p>
<br>
<div class="restore-item-table">
<p><i class="fas fa-exclamation-triangle"></i> <span>Importante:<span></span></span></p>
<p>Si tu cuenta aparece como sancionada o cerrada al intentar ingresar al reino, pero tu historial no tiene ninguna sanción activa, significa que has tenido un bloqueo automático temporal por demasiados intentos de inicio de sesión fallidos.</p>
<p>Espera al menos 10 minutos para volver a intentar y asegúrate de usar nombre de cuenta y contraseña correctas.</p>
</div>
<br>
<p>Para realizar un reclamo de sanción, por favor ingresa en nuestro <a href="https://foro.novawow.com/" target="_blank" rel="noopener noreferrer">foro</a> y visita la sección Reclamaciones.</p>
</div>
</div>
<div class="box-content">
<div class="title-box-content"><h2>Historial de baneos</h2></div>
<div class="body-box-content info-box-light">
<table class="max-center-table">
<tbody><tr>
<td class="centered"><span>No hay baneos</span></td>
</tr>
</tbody></table>
</div>
</div>
<div class="box-content">
<div class="title-box-content"><h2>Historial de muteos</h2></div>
<div class="body-box-content info-box-light">
<table class="max-center-table">
<tbody><tr>
<td class="centered"><span>No hay muteos</span></td>
</tr>
</tbody></table>
</div>
</div>
<div class="middle-content">
<div class="body-content">
<div class="title-content">
<h1>Historial de sanciones</h1>
</div>
<div class="box-content">
<div class="title-box-content">
<h2>Información</h2>
<a class="back-to-account" href="my-account">Regresar a mi cuenta</a>
<a class="back-to-account-responsive" href="my-account">Regresar</a>
</div>
<div class="body-box-content justified">
<br>
<p>Aquí podrás ver todas las sanciones que ha tenido la cuenta.</p>
<p>Si tienes una sanción vigente, verás que su estado indica <span class="red-info2">Activo</span>.</p>
<p>Para evitar que tu cuenta sea sancionada, recuerda respetar las normas de nuestro servidor.</p>
<br>
</div>
</div>
</div>
<!-- Historial de baneos -->
<div class="box-content">
<div class="title-box-content">
<h2>Historial de baneos</h2>
</div>
<div class="body-box-content info-box-light">
<table class="max-center-table" style="width: 100%; border-collapse: collapse;">
<thead>
<tr>
<th style="text-align: left; padding: 10px; border: 1px solid #ddd;">Fecha de Baneo</th>
<th style="text-align: left; padding: 10px; border: 1px solid #ddd;">Fecha de Desbaneo</th>
<th style="text-align: left; padding: 10px; border: 1px solid #ddd;">Motivo</th>
<th style="text-align: left; padding: 10px; border: 1px solid #ddd;">Estado</th>
<th style="text-align: left; padding: 10px; border: 1px solid #ddd;">
{% if account_bans and character_bans %}
Cuenta / Personaje
{% elif account_bans %}
Cuenta
{% elif character_bans %}
Personaje
{% else %}
Fuente
{% endif %}
</th>
</tr>
</thead>
<tbody>
{% if account_bans %}
{% for ban in account_bans %}
<tr>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ban.0|default:"No disponible" }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ban.1|default:"No disponible" }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ban.2 }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">
{% if ban.3 == 1 %}
<span class="red-info2">Activo<span>
{% else %}
<span class="green-info2">Inactivo</span>
{% endif %}
</td>
<td style="padding: 10px; border: 1px solid #ddd;">Cuenta: {{ ban.4 }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="5" style="text-align: center; padding: 10px; border: 1px solid #ddd;">No hay baneos en la cuenta.</td>
</tr>
{% endif %}
{% if character_bans %}
{% for ban in character_bans %}
<tr>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ban.0|default:"No disponible" }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ban.1|default:"No disponible" }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">{{ ban.2 }}</td>
<td style="padding: 10px; border: 1px solid #ddd;">
{% if ban.3 == 1 %}
Activo
{% else %}
<span class="green-info2">Inactivo</span>
{% endif %}
</td>
<td style="padding: 10px; border: 1px solid #ddd;">Personaje: {{ ban.4 }}</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan="5" style="text-align: center; padding: 10px; border: 1px solid #ddd;">No hay baneos en los personajes.</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
</div>
+49 -32
View File
@@ -1,36 +1,53 @@
<div class="main-page">
<div class="middle-content">
<div class="body-content">
<div class="title-content"><h1>Recuperar personaje</h1></div>
<div class="box-content">
<div class="title-box-content"><h2>Información</h2><a class="back-to-account" href="my-account">Regresar a mi cuenta</a><a class="back-to-account-responsive" href="my-account">Regresar</a></div>
<div class="body-box-content justified">
<br>
<p>La herramienta <span>Recuperar personaje</span> te permite restaurar un personaje que haya sido borrado.</p>
<br>
<p>Sólo disponible para personajes que cumplan las siguientes dos condiciones:</p>
<p>- Nivel superior a 60.</p>
<p>- Borrado en un período menor a 30 días.</p>
<p>Para personajes <span class="death-knight">Caballeros de la Muerte:</span></p>
<p>- No es posible recuperar un personaje Caballero de la Muerte si ya hay otro activo en la cuenta.</p>
<br>
<p>Cuando ingreses a la lista de personajes de tu cuenta, verás al personaje recuperado.</p>
<br>
<fieldset>
<legend>NOTA</legend>
<div class="separate2">
<p>Personajes borrados que no cumplan dichas condiciones ya no son recuperables.</p>
<p>Se puede repetir la acción en un mismo personaje cada 12 horas.</p>
</div>
</fieldset>
<div class="centered">
<br>
<br>
<span>No hay personajes</span>
<br>
</div>
<div class="middle-content">
<div class="body-content">
<div class="title-content"><h1>Recuperar personaje</h1></div>
<div class="box-content">
<div class="title-box-content">
<h2>Información</h2>
<a class="back-to-account" href="my-account">Regresar a mi cuenta</a>
<a class="back-to-account-responsive" href="my-account">Regresar</a>
</div>
<div class="body-box-content justified">
<br>
<p>La herramienta <span>Recuperar personaje</span> te permite restaurar un personaje que haya sido borrado.</p>
<br>
<p>Sólo disponible para personajes que cumplan las siguientes dos condiciones:</p>
<p>- Nivel superior a 60.</p>
<p>- Borrado en un período menor a 30 días.</p>
<p>Para personajes <span class="death-knight">Caballeros de la Muerte:</span></p>
<p>- No es posible recuperar un personaje Caballero de la Muerte si ya hay otro activo en la cuenta.</p>
<br>
<p>Cuando ingreses a la lista de personajes de tu cuenta, verás al personaje recuperado.</p>
<br>
<fieldset>
<legend>NOTA</legend>
<div class="separate2">
<p>Personajes borrados que no cumplan dichas condiciones ya no son recuperables.</p>
<p>Se puede repetir la acción en un mismo personaje cada 12 horas.</p>
</div>
</div>
</fieldset>
<div class="centered">
<br><br>
{% if message %}
<p>{{ message }}</p>
{% endif %}
{% if character_list %}
<form method="POST">
{% csrf_token %}
<select name="guid">
{% for character in character_list %}
<option value="{{ character.guid }}">{{ character.name }} (LvL {{ character.level }})</option>
{% endfor %}
</select>
<button type="submit">Restaurar Personaje</button>
</form>
{% else %}
<span>No hay personajes eliminados disponibles para restaurar.</span>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
+48 -40
View File
@@ -1,45 +1,53 @@
<div class="main-page">
<div class="middle-content">
<div class="modal centered" id="modal-div">
<div class="modal-content">
<span class="close"><i class="fas fa-times"></i></span>
<span class="red-form-response" id="restored-item-response"></span>
</div>
<div class="middle-content">
<div class="body-content">
<div class="title-content"><h1>Recuperar ítems</h1></div>
<div class="box-content">
<div class="title-box-content">
<h2>Información</h2>
<a class="back-to-account" href="my-account">Regresar a mi cuenta</a>
</div>
<div class="body-box-content justified">
<p>La herramienta <span>Recuperar ítems</span> te permite recuperar ítems que hayan sido borrados.</p>
<p>Sólo disponible para ítems de calidad: <span class="q3">Raro</span> | <span class="q4">Épico</span> | <span class="q5">Legendaria</span> | <span class="q6">Artefacto</span> | <span class="q6">Reliquia</span>.</p>
<fieldset>
<legend>NOTA</legend>
<p>Ítems que no cumplan las condiciones no son recuperables.</p>
<p>Se pueden consultar ítems borrados en un mismo personaje cada 8 horas.</p>
</fieldset>
<div class="body-content">
<div class="title-content"><h1>Recuperar ítems</h1></div>
<div class="box-content">
<div class="title-box-content"><h2>Información</h2><a class="back-to-account" href="my-account">Regresar a mi cuenta</a><a class="back-to-account-responsive" href="my-account">Regresar</a></div>
<div class="body-box-content justified">
<br>
<p>La herramienta <span>Recuperar ítems</span> te permite recuperar ítems que hayan sido borrados.</p>
<br>
<p>Sólo disponible para ítems que cumplan las siguientes condiciones:</p>
<p>- De calidad: <span class="q3">Raro</span> | <span class="q4">Épico</span> | <span class="q5">Legendaria</span> | <span class="q6">Artefacto</span> | <span class="q6">Reliquia</span>.</p>
<p>- Equipable.</p>
<p>- Se liga al recogerlo.</p>
<p>- Borrado en un período menor a 7 días.</p>
<br>
<fieldset>
<legend>NOTA</legend>
<div class="separate2">
<p>Ítems borrados que no cumplan dichas condiciones ya no son recuperables.</p>
<p>Se pueden consultar ítems borrados en un mismo personaje cada 8 horas.</p>
</div>
</fieldset>
<div class="centered">
<br>
<br>
<p>Cada ítem requiere <span>100</span> <span class="yellow-info">PD</span></p>
<br>
<div id="char-selection">
<span>No tienes PD suficientes</span>
</div>
<br>
</div>
</div>
</div>
<!-- Selección de personaje -->
<form method="GET" action="{% url 'restore-items' %}">
<label for="character_name">Selecciona un personaje:</label>
<select name="character_name" id="character_name" required>
<option value="" disabled selected>Selecciona un personaje</option>
{% for character in characters %}
<option value="{{ character }}" {% if character == selected_character %}selected{% endif %}>{{ character }}</option>
{% endfor %}
</select>
<button type="submit">Buscar Ítems Eliminados</button>
</form>
{% if deleted_items %}
<form method="POST" action="{% url 'restore-items' %}">
{% csrf_token %}
<input type="hidden" name="character_name" value="{{ selected_character }}">
<label for="recover_id">Selecciona un ítem para restaurar:</label>
<select name="recover_id" id="recover_id" required>
{% for item in deleted_items %}
<option value="{{ item.recover_id }}">
Item: {{ item.item_name }} ({{ item.item_id }}):{{ item.count }}
</option>
{% endfor %}
</select>
<button type="submit">Recuperar Ítem</button>
</form>
{% elif selected_character %}
<p>No hay ítems eliminados disponibles para restaurar.</p>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
+247 -17
View File
@@ -199,19 +199,19 @@ def login_view(request):
password = form.cleaned_data['password']
ip_address = request.META.get('REMOTE_ADDR') # Obtener la IP del cliente
# Llamar a la funci¨®n de autenticaci¨®n
# Llamar a la función de autenticación
if authenticate(username, password):
# Guardar el intento exitoso
log_login_attempt(username, "Exito", ip_address)
# Guardar el nombre de usuario en may¨²sculas en la sesi¨®n
# Guardar el nombre de usuario en mayúsculas en la sesión
request.session['username'] = username.upper()
return JsonResponse({'success': True})
else:
# Guardar el intento fallido
log_login_attempt(username, "Fracaso", ip_address)
return JsonResponse({'success': False, 'error': "Usuario o contrase?a incorrectos"})
return JsonResponse({'success': False, 'error': "Usuario o contraseña incorrectos"})
else:
return JsonResponse({'success': False, 'error': "Formulario no valido"})
@@ -675,7 +675,6 @@ def my_account(request):
})
def get_account_status(account_id):
with connections['acore_auth'].cursor() as cursor:
# Obtener información de la tabla 'account'
@@ -737,7 +736,7 @@ def get_account_status(account_id):
unban_timestamp, current_timestamp = ban_data
remaining_time = unban_timestamp - current_timestamp
user_info['is_banned'] = True
user_info['unban_date'] = datetime.datetime.fromtimestamp(unban_timestamp).strftime("%A %d de %B del %Y a las %H:%M:%S Horas")
user_info['unban_date'] = datetime.fromtimestamp(unban_timestamp).strftime("%A %d de %B del %Y a las %H:%M:%S Horas")
user_info['remaining_time'] = remaining_time
else:
user_info['is_banned'] = False
@@ -1259,17 +1258,173 @@ def rename_cancel_view(request):
return redirect('login')
return render(request, 'account/rename_cancel.html')
@csrf_exempt
def restore_character_view(request):
# Verifica si el usuario está autenticado y obtiene su nombre de usuario
username = request.session.get('username')
if not username:
return redirect('login')
return render(request, 'account/restore_character.html')
return redirect('login') # Redirige si no hay sesión
# Obtener la ID de cuenta del usuario conectado a partir del sistema de autenticación personalizada
try:
with connections['acore_auth'].cursor() as cursor:
cursor.execute("SELECT id FROM account WHERE username = %s", [username])
result = cursor.fetchone()
if not result:
# Si no se encuentra la cuenta, mostrar un error
messages.error(request, "No se encontró la cuenta asociada a ese usuario.")
return redirect('login') # Redirige al login si no se encuentra la cuenta
account_id = result[0] # Obtén el ID de la cuenta
except Exception as e:
messages.error(request, f"Ocurrió un error al recuperar la cuenta: {str(e)}")
return redirect('login')
# Verificar si se ha seleccionado un personaje para restaurar
if request.method == 'POST':
# Obtener el GUID del personaje seleccionado para restaurar
guid = request.POST.get('guid')
if not guid:
messages.error(request, "No se seleccionó un personaje para restaurar.")
return redirect('restore-character')
# Aquí es donde usamos SOAP en lugar de la actualización directa
try:
command = f".char del restore {guid}" # Comando SOAP para restaurar el personaje
response = execute_soap_command(command) # Ejecuta el comando SOAP
# Si la respuesta es exitosa, informamos al usuario
if response == "success":
messages.success(request, "Personaje restaurado exitosamente.")
return redirect('my-account') # Redirigir a la cuenta del usuario
else:
messages.error(request, "Hubo un error al restaurar el personaje.")
return redirect('restore-character')
except Exception as e:
messages.error(request, f"Hubo un error al intentar restaurar el personaje: {str(e)}")
return redirect('restore-character')
# Realizar la consulta para obtener los personajes eliminados de la cuenta
with connections['acore_characters'].cursor() as cursor:
cursor.execute("""
SELECT guid, level, deleteInfos_Name, deleteInfos_Account, deleteDate
FROM characters
WHERE deleteInfos_Account = %s AND deleteDate IS NOT NULL
""", [account_id]) # Filtra por account_id
# Obtener los resultados de la consulta
deleted_characters = cursor.fetchall()
# Si no hay personajes eliminados
if not deleted_characters:
return render(request, 'account/restore_character.html', {
'message': 'No hay personajes eliminados que puedas recuperar.'
})
# Crear un listado de personajes eliminados con su nombre y nivel (si lo tienes)
character_list = []
for character in deleted_characters:
guid, level, name, account_id, delete_date = character
# Agregar la información del personaje al listado
character_list.append({
'name': name,
'guid': guid,
'level': level,
'delete_date': delete_date
})
# Renderiza la plantilla con los personajes eliminados
return render(request, 'account/restore_character.html', {
'character_list': character_list
})
@csrf_exempt
def restore_items_view(request):
username = request.session.get('username')
if not username:
return redirect('login')
return render(request, 'account/restore_items.html')
characters = []
deleted_items = []
selected_character = None
try:
# Obtener la ID de cuenta del usuario
with connections['acore_auth'].cursor() as cursor:
cursor.execute("SELECT id FROM account WHERE username = %s", [username])
result = cursor.fetchone()
if result:
account_id = result[0]
else:
messages.error(request, "No se encontró la cuenta asociada.")
return redirect('login')
# Obtener los personajes del usuario
with connections['acore_characters'].cursor() as cursor:
cursor.execute("SELECT name FROM characters WHERE account = %s", [account_id])
characters = [row[0] for row in cursor.fetchall()]
except Exception as e:
messages.error(request, f"Error al obtener los personajes: {str(e)}")
if request.method == "GET" and "character_name" in request.GET:
selected_character = request.GET.get("character_name")
if selected_character:
try:
# Ejecutar comando SOAP para obtener ítems eliminados del personaje seleccionado
command = f".item restore list {selected_character}"
response = execute_soap_command(command)
# Procesar la respuesta del servidor
for line in response.split("\n"):
match = re.search(r"Recover id: (\d+) \| Item: (.+) \((\d+)\) \| Count: (\d+)", line)
if match:
recover_id = match.group(1)
item_name = match.group(2)
item_id = match.group(3)
count = match.group(4)
deleted_items.append({
"recover_id": recover_id,
"item_name": item_name,
"item_id": item_id,
"count": count
})
except Exception as e:
messages.error(request, f"Error al obtener los ítems eliminados: {str(e)}")
if request.method == "POST":
recover_id = request.POST.get("recover_id")
selected_character = request.POST.get("character_name")
if not recover_id or not selected_character:
messages.error(request, "Selecciona un personaje y un ítem para restaurar.")
return redirect('restore-items')
try:
# Ejecutar comando SOAP para restaurar el ítem
command = f".item restore {recover_id} {selected_character}"
response = execute_soap_command(command)
if "success" in response.lower():
messages.success(request, "Ítem restaurado exitosamente.")
else:
messages.error(request, "No se pudo restaurar el ítem.")
except Exception as e:
messages.error(request, f"Error al restaurar el ítem: {str(e)}")
return redirect('restore-items')
return render(request, 'account/restore_items.html', {
'characters': characters,
'deleted_items': deleted_items,
'selected_character': selected_character
})
def quest_character_view(request):
username = request.session.get('username')
@@ -1288,7 +1443,7 @@ def customize_character_view(request):
if not username:
return redirect('login')
# ?? Obtener informaci��n de la cuenta desde acore_auth
# ?? Obtener información de la cuenta desde acore_auth
with connections['acore_auth'].cursor() as cursor:
cursor.execute("""
SELECT id, email, last_ip FROM account WHERE username = %s
@@ -2318,13 +2473,20 @@ def d_points_view(request):
def points_history_view(request):
return render(request, 'account/points_history.html')
def format_datetime(timestamp):
if timestamp:
return datetime.utcfromtimestamp(timestamp).strftime('%d-%m-%Y %H:%M:%S')
return "No disponible"
def trans_history_view(request):
"""
Vista para mostrar el historial de transacciones de la cuenta y sus personajes.
"""
username = request.session.get('username') # Obtener el nombre de usuario de la sesi¨®n
username = request.session.get('username') # Obtener el nombre de usuario de la sesión
if username:
# Filtramos las transacciones que corresponden al usuario y a los personajes asociados
@@ -2335,22 +2497,90 @@ def trans_history_view(request):
# Renderizamos la vista con el historial de transacciones
return render(request, 'account/trans_history.html', {'transactions': transactions})
def ban_history_view(request):
return render(request, 'account/ban_history.html')
def get_account_id(username):
# Consulta para obtener el account_id a partir del username
with connections['acore_auth'].cursor() as cursor:
cursor.execute("""
SELECT id
FROM account
WHERE username = %s
""", [username])
result = cursor.fetchone()
return result[0] if result else None
def get_ban_history(account_id):
# Consulta de baneos de la cuenta
with connections['acore_auth'].cursor() as cursor:
cursor.execute("""
SELECT bandate, unbandate, banreason, active, account.username
FROM account_banned
JOIN account ON account_banned.id = account.id
WHERE account_banned.id = %s
""", [account_id])
account_bans = cursor.fetchall()
# Formatear las fechas
account_bans = [(format_datetime(ban[0]), format_datetime(ban[1]), ban[2], ban[3], ban[4]) for ban in account_bans]
# Consulta de baneos de los personajes asociados a la cuenta
with connections['acore_characters'].cursor() as cursor:
cursor.execute("""
SELECT bandate, unbandate, banreason, active, characters.name
FROM character_banned
JOIN characters ON character_banned.guid = characters.guid
WHERE characters.account = %s
""", [account_id])
character_bans = cursor.fetchall()
# Formatear las fechas
character_bans = [(format_datetime(ban[0]), format_datetime(ban[1]), ban[2], ban[3], ban[4]) for ban in character_bans]
return account_bans, character_bans
def calculate_unmute_date(mute_date, mute_duration):
if isinstance(mute_date, int):
mute_date = datetime.fromtimestamp(mute_date)
return mute_date + timedelta(seconds=mute_duration)
def ban_history_view(request):
# Verificar si el usuario tiene un `username` en la sesión
username = request.session.get('username')
if not username:
return redirect('login')
# Obtener el account_id a partir del username
account_id = get_account_id(username)
if not account_id:
return redirect('login') # O redirigir a otra página si no se encuentra el account_id
print(f"Account ID: {account_id}") # Depurar el account_id
account_bans, character_bans = get_ban_history(account_id)
return render(request, 'account/ban_history.html', {
'account_bans': account_bans,
'character_bans': character_bans,
'now': datetime.now(),
})
def security_history_view(request):
username = request.session.get('username')
if not username:
return redirect('login') # Si no est¨¢ autenticado, redirigir al login
return redirect('login') # Si no está autenticado, redirigir al login
# Obtener los ¨²ltimos 20 intentos de inicio de sesi¨®n
# Obtener los últimos 20 intentos de inicio de sesión
login_attempts = LoginAttempt.objects.filter(username=username).order_by('-timestamp')[:20]
return render(request, 'account/security_history.html', {'login_attempts': login_attempts})
def log_login_attempt(username, status, ip_address):
# Registrar intento de inicio de sesi¨®n en el historial
# Registrar intento de inicio de sesión en el historial
LoginAttempt.objects.create(
username=username,
status=status,