diff --git a/home/__pycache__/models.cpython-313.pyc b/home/__pycache__/models.cpython-313.pyc index 37632d7..5b1bf2b 100644 Binary files a/home/__pycache__/models.cpython-313.pyc and b/home/__pycache__/models.cpython-313.pyc differ diff --git a/home/__pycache__/urls.cpython-313.pyc b/home/__pycache__/urls.cpython-313.pyc index bd4ad0c..a07f2f1 100644 Binary files a/home/__pycache__/urls.cpython-313.pyc and b/home/__pycache__/urls.cpython-313.pyc differ diff --git a/home/__pycache__/views.cpython-313.pyc b/home/__pycache__/views.cpython-313.pyc index ab5a134..449bca5 100644 Binary files a/home/__pycache__/views.cpython-313.pyc and b/home/__pycache__/views.cpython-313.pyc differ diff --git a/home/models.py b/home/models.py index 1ed077f..f986ee9 100644 --- a/home/models.py +++ b/home/models.py @@ -16,6 +16,10 @@ class Noticia(models.Model): def __str__(self): return self.titulo + +class UnstuckHistory(models.Model): + character_name = models.CharField(max_length=50) + used_at = models.DateTimeField() class ClienteCategoria(models.Model): diff --git a/home/static/nw-themes/nw-ryu/nw-js-handlers/change_email_response.js b/home/static/nw-themes/nw-ryu/nw-js-handlers/change_email_response.js index cc0b6bb..ffed7b2 100644 --- a/home/static/nw-themes/nw-ryu/nw-js-handlers/change_email_response.js +++ b/home/static/nw-themes/nw-ryu/nw-js-handlers/change_email_response.js @@ -1,34 +1,45 @@ $(document).ready(function() { - $('#nw-change-email-form').on('submit', function(e) { - e.preventDefault(); // Evitar el envío por defecto del formulario + function handleResponse(response, button, originalText) { + $("#change-email-response").html(response.message).hide().slideDown(); + + if (response.redirect_url) { + setTimeout(function() { + window.location.href = response.redirect_url; + }, 2000); + return; + } + + if (response.success) { + button.html("Proceso completado"); + } else { + setTimeout(function() { + $("#change-email-response").slideUp(function() { + $("#change-email-response").empty(); + button.html(originalText); + button.prop("disabled", false); + }); + }, 5000); + } + } + + // Manejar los enlaces de confirmación + $('.confirm-link').on('click', function(e) { + e.preventDefault(); $("#change-email-response").empty(); - var form = $(this); - var button = $('.change-email-button'); + var button = $(this); + var url = button.attr('href'); var originalText = button.html(); - button.html("Cambiando correo..."); + button.html("Verificando..."); button.prop("disabled", true); $.ajax({ - type: 'POST', - url: form.attr('action'), - data: form.serialize(), // Serializar los datos correctamente + type: 'GET', + url: url, dataType: 'json', success: function(response) { - $("#change-email-response").html(response.message).hide().slideDown(); - - if (response.success) { - button.html("Correo cambiado"); - } else { - setTimeout(function() { - $("#change-email-response").slideUp(function() { - $("#change-email-response").empty(); - button.html(originalText); - button.prop("disabled", false); - }); - }, 5000); - } + handleResponse(response, button, originalText); }, error: function(xhr, status, error) { console.error("Error:", error); diff --git a/home/static/nw-themes/nw-ryu/nw-js-handlers/unstuck_response.js b/home/static/nw-themes/nw-ryu/nw-js-handlers/unstuck_response.js new file mode 100644 index 0000000..ab7bb27 --- /dev/null +++ b/home/static/nw-themes/nw-ryu/nw-js-handlers/unstuck_response.js @@ -0,0 +1,41 @@ +$(function() { + $('.unstuck-button').on('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + $("#unstuck-response").empty(); + + var button = $(this); + var originalText = button.html(); + var data = $("#uw-unstuck-form").serialize(); + + button.html("Desbloqueando..."); + button.prop("disabled", true); + + $.ajax({ + type: 'POST', + url: '', + data: data, + dataType: 'json', + success: function(data) { + $("#unstuck-response").html(data.message).hide().slideDown(); + + if (data.success) { + button.html("Desbloqueado"); + setTimeout(function() { + $("#uw-unstuck-form")[0].reset(); + button.html(originalText); + button.prop("disabled", false); + }, 5000); + } else { + button.html(originalText); + button.prop("disabled", false); + } + }, + error: function() { + $("#unstuck-response").html('Algo ha salido mal. Inténtelo más tarde.'); + button.html(originalText); + button.prop("disabled", false); + } + }); + }); +}); diff --git a/home/templates/account/confirm_new_email_success.html b/home/templates/account/confirm_new_email_success.html new file mode 100644 index 0000000..40745ea --- /dev/null +++ b/home/templates/account/confirm_new_email_success.html @@ -0,0 +1,9 @@ + + + {% include 'partials/head.html' %} + {% include 'partials/header.html' %} + {% include 'partials/video.html' %} + {% include 'partials/confirm_new_email_success.html' %} + {% include 'partials/social.html' %} + {% include 'partials/footer.html' %} + {% include 'partials/final.html' %} \ No newline at end of file diff --git a/home/templates/account/confirm_old_email_success.html b/home/templates/account/confirm_old_email_success.html new file mode 100644 index 0000000..e507d48 --- /dev/null +++ b/home/templates/account/confirm_old_email_success.html @@ -0,0 +1,9 @@ + + + {% include 'partials/head.html' %} + {% include 'partials/header.html' %} + {% include 'partials/video.html' %} + {% include 'partials/confirm_old_email_success.html' %} + {% include 'partials/social.html' %} + {% include 'partials/footer.html' %} + {% include 'partials/final.html' %} \ No newline at end of file diff --git a/home/templates/account/expired_link.html b/home/templates/account/expired_link.html new file mode 100644 index 0000000..1e11adf --- /dev/null +++ b/home/templates/account/expired_link.html @@ -0,0 +1,9 @@ + + + {% include 'partials/head.html' %} + {% include 'partials/header.html' %} + {% include 'partials/video.html' %} + {% include 'partials/expired_link.html' %} + {% include 'partials/social.html' %} + {% include 'partials/footer.html' %} + {% include 'partials/final.html' %} \ No newline at end of file diff --git a/home/templates/account/unstuck_character.html b/home/templates/account/unstuck_character.html new file mode 100644 index 0000000..4fa7a83 --- /dev/null +++ b/home/templates/account/unstuck_character.html @@ -0,0 +1,9 @@ + + + {% include 'partials/head.html' %} + {% include 'partials/header.html' %} + {% include 'partials/video.html' %} + {% include 'partials/unstuck_character.html' %} + {% include 'partials/social.html' %} + {% include 'partials/footer.html' %} + {% include 'partials/final.html' %} \ No newline at end of file diff --git a/home/templates/partials/confirm_new_email_success.html b/home/templates/partials/confirm_new_email_success.html new file mode 100644 index 0000000..918a284 --- /dev/null +++ b/home/templates/partials/confirm_new_email_success.html @@ -0,0 +1,15 @@ + + + + + Confirmación de Nuevo Correo + + + +
+

Cambio de Correo Completado

+

El cambio de correo ha sido confirmado y completado con éxito.

+ Regresar a Mi Cuenta +
+ + diff --git a/home/templates/partials/confirm_old_email_success.html b/home/templates/partials/confirm_old_email_success.html new file mode 100644 index 0000000..b96b992 --- /dev/null +++ b/home/templates/partials/confirm_old_email_success.html @@ -0,0 +1,15 @@ + + + + + Confirmación de Correo Anterior + + + +
+

Confirmación de Correo Anterior Exitosa

+

Has confirmado tu correo anterior. Se ha enviado un enlace al nuevo correo para completar el cambio.

+ Regresar a Mi Cuenta +
+ + diff --git a/home/templates/partials/expired_link.html b/home/templates/partials/expired_link.html new file mode 100644 index 0000000..286eab0 --- /dev/null +++ b/home/templates/partials/expired_link.html @@ -0,0 +1,27 @@ +
+
+
+
+

Enlace Expirado

+
+
+
+

Enlace Expirado

+ + +
+
+
+
+
+ Lo sentimos, el enlace que ha utilizado ha expirado o no es válido. +
+
+ No hay códigos de promoción disponibles + +
+
+
+
+
+
diff --git a/home/templates/partials/head.html b/home/templates/partials/head.html index 4fdc02b..576b91f 100644 --- a/home/templates/partials/head.html +++ b/home/templates/partials/head.html @@ -52,6 +52,7 @@ + diff --git a/home/templates/partials/my-account.html b/home/templates/partials/my-account.html index 65d2334..03f8752 100644 --- a/home/templates/partials/my-account.html +++ b/home/templates/partials/my-account.html @@ -207,6 +207,201 @@
+ {% for character in characters %} +
OPCIONES DE PERSONAJE + + +
+ + +
HISTORIALES
diff --git a/home/templates/partials/unstuck_character.html b/home/templates/partials/unstuck_character.html new file mode 100644 index 0000000..39ba380 --- /dev/null +++ b/home/templates/partials/unstuck_character.html @@ -0,0 +1,43 @@ +
+
+
+
+

Desbloquear personaje

+
+
+
+

Información

+ + +
+
+

La herramienta Desbloquear personaje te permite desbloquear un personaje que esté atascado y lo mueve a la ubicación de su Piedra Hogar.

+

Usa esta opción cuando tu personaje se encuentre atrapado y ninguna otra opción te sirva.

+
+ NOTA +
+

Se requiere que el personaje esté desconectado.

+

Se puede repetir la acción en un mismo personaje cada 12 horas.

+
+
+
+

Escoge el personaje que quieres desbloquear

+
+ {% csrf_token %} + +
+ + +
+
+
+
+
+
+
+
diff --git a/home/urls.py b/home/urls.py index 6be487d..41f8a1f 100644 --- a/home/urls.py +++ b/home/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ path('change-email/', views.change_email_view, name='change-email'), path('confirm-old-email/', views.confirm_old_email_view, name='confirm-old-email'), path('confirm-new-email/', views.confirm_new_email_view, name='confirm-new-email'), + path('expired-link/', views.expired_link_view, name='expired-link'), path('promo-code/', views.promo_code_view, name='promo_code'), path('transfer-d-points/', views.transfer_d_points_view, name='transfer_d_points'), path('rename-guild/', views.rename_guild_view, name='rename-guild'), @@ -39,6 +40,7 @@ urlpatterns = [ path('activate-account', views.activate_account_view, name='activate_account'), path('help', views.help_view, name='help'), path('novawow-players', views.novawow_players_view, name='novawow_players'), + path('unstuck-character/', views.unstuck_character_view, name='unstuck-character'), ] diff --git a/home/views.py b/home/views.py index aee4310..83187e6 100644 --- a/home/views.py +++ b/home/views.py @@ -5,12 +5,13 @@ import binascii import os import re from django.shortcuts import render, redirect +from django.urls import reverse from django.http import HttpResponse, JsonResponse from django.db import connections from django.contrib import messages from django.contrib.auth import logout from django import forms -from .models import Noticia, ClienteCategoria, ServerSelection, RecruitAFriend, DownloadClientPage, ContentCreator, RecruitReward, ClaimedReward, AccountActivation, SecurityToken, GuildRenameSettings, VoteSite, VoteLog, HomeApiPoints +from .models import Noticia, ClienteCategoria, ServerSelection, RecruitAFriend, DownloadClientPage, ContentCreator, RecruitReward, ClaimedReward, AccountActivation, SecurityToken, GuildRenameSettings, VoteSite, VoteLog, HomeApiPoints, UnstuckHistory from django.views.decorators.csrf import csrf_exempt import logging from datetime import datetime, timedelta @@ -1001,22 +1002,22 @@ def change_email_view(request): # Validar los campos if not all([current_password, current_email_input, new_email, conf_new_email, token]): - return JsonResponse({'success': False, 'message': 'Por favor, complete todos los campos.'}) + return JsonResponse({'success': False, 'message': 'Por favor, complete todos los campos.'}) if current_email_input != current_email: - return JsonResponse({'success': False, 'message': 'El correo actual no es correcto.'}) + return JsonResponse({'success': False, 'message': 'El correo actual no es correcto.'}) if new_email != conf_new_email: - return JsonResponse({'success': False, 'message': 'Los correos no coinciden.'}) + return JsonResponse({'success': False, 'message': 'Los correos no coinciden.'}) if not re.match(r'^[a-zA-Z0-9._%+-]+@gmail\.com$', new_email): - return JsonResponse({'success': False, 'message': 'El nuevo correo no es válido.'}) + return JsonResponse({'success': False, 'message': 'El nuevo correo no es válido.'}) if not security_token or security_token.token != token: - return JsonResponse({'success': False, 'message': 'Token de seguridad incorrecto.'}) + return JsonResponse({'success': False, 'message': 'Token de seguridad incorrecto.'}) if not authenticate(username, current_password): - return JsonResponse({'success': False, 'message': 'Contraseña incorrecta.'}) + return JsonResponse({'success': False, 'message': 'Contraseña incorrecta.'}) # Generar hashes old_email_hash = secrets.token_urlsafe(16) @@ -1049,7 +1050,7 @@ def change_email_view(request): context=context_old_email ) - return JsonResponse({'success': True, 'message': 'Se ha enviado un correo de confirmación al correo actual.'}) + return JsonResponse({'success': True, 'message': 'Se ha enviado un correo de confirmación al correo actual.'}) return render(request, 'account/change_email.html') @@ -1059,7 +1060,11 @@ def confirm_old_email_view(request): activation = AccountActivation.objects.filter(old_email_hash=hash, is_used=False).first() if not activation or activation.is_expired(): - return HttpResponse('Enlace no válido o expirado.', status=400) + return JsonResponse({ + 'success': False, + 'message': 'Enlace no válido o expirado.', + 'redirect_url': reverse('expired-link') + }) # Marcar solo el old_email_hash como usado activation.is_used = True @@ -1079,15 +1084,18 @@ def confirm_old_email_view(request): context=context_new_email ) - return HttpResponse('Correo confirmado. Se ha enviado un enlace al nuevo correo.') - + return JsonResponse({'success': True, 'message': 'Correo confirmado. Se ha enviado un enlace al nuevo correo.'}) def confirm_new_email_view(request): hash = request.GET.get('hash') activation = AccountActivation.objects.filter(hash=hash, is_new_email_used=False).first() if not activation or activation.is_expired(): - return HttpResponse('Enlace no válido o expirado.', status=400) + return JsonResponse({ + 'success': False, + 'message': 'Enlace no válido o expirado.', + 'redirect_url': reverse('expired-link') + }) # Marcar solo el new_email_hash como usado activation.is_new_email_used = True @@ -1114,11 +1122,72 @@ def confirm_new_email_view(request): context=context_old_email ) - return HttpResponse('El cambio de correo ha sido confirmado y completado con éxito.') + return JsonResponse({'success': True, 'message': 'El cambio de correo ha sido confirmado y completado con éxito.'}) + +def expired_link_view(request): + return render(request, 'account/expired_link.html') - def promo_code_view(request): return render(request, 'account/promo_code.html') + +from django.utils.timezone import now +from datetime import timedelta + +def unstuck_character_view(request): + username = request.session.get('username') + if not username: + return redirect('login') + + # Obtener personajes del usuario desde la base de datos `acore_characters` + with connections['acore_characters'].cursor() as cursor: + cursor.execute(""" + SELECT name, online + FROM characters + WHERE account = ( + SELECT id + FROM acore_auth.account + WHERE username = %s + ) + """, [username]) + characters_data = cursor.fetchall() + characters = {row[0]: row[1] for row in characters_data} # Diccionario con nombre y estado de conexión + + if request.method == 'POST': + character_name = request.POST.get('character') + + # Validar si el personaje fue seleccionado + if not character_name: + return JsonResponse({'success': False, 'message': 'Por favor, seleccione un personaje.'}) + + # Verificar si el personaje pertenece al usuario + if character_name not in characters: + return JsonResponse({'success': False, 'message': 'No tienes permiso para desbloquear este personaje.'}) + + # Verificar si el personaje está desconectado + if characters[character_name] == 1: # Si está conectado + return JsonResponse({'success': False, 'message': 'El personaje debe estar desconectado para ser desbloqueado.'}) + + # Verificar el historial de uso del comando + last_unstuck = UnstuckHistory.objects.filter(character_name=character_name).order_by('-used_at').first() + if last_unstuck and last_unstuck.used_at > now() - timedelta(hours=12): + return JsonResponse({'success': False, 'message': 'El personaje solo puede desbloquearse una vez cada 12 horas.'}) + + # Ejecutar el comando SOAP + command = f".unstuck {character_name} startzone" + response = execute_soap_command(command) + + if response is None: + return JsonResponse({'success': False, 'message': 'Error de comunicación con el servidor. Intente nuevamente más tarde.'}) + + # Analizar la respuesta + if f"You are summoning {character_name} (offline)" in response: + # Registrar el uso del comando + UnstuckHistory.objects.create(character_name=character_name, used_at=now()) + return JsonResponse({'success': True, 'message': f'El personaje {character_name} ha sido desbloqueado con éxito.'}) + else: + return JsonResponse({'success': False, 'message': f'Error: {response}'}) + + return render(request, 'account/unstuck_character.html', {'characters': characters.keys()}) def transfer_d_points_view(request): # Renderizar la plantilla para la transferencia de puntos diff --git a/novawow/__pycache__/settings.cpython-313.pyc b/novawow/__pycache__/settings.cpython-313.pyc index 93e06ad..b334c19 100644 Binary files a/novawow/__pycache__/settings.cpython-313.pyc and b/novawow/__pycache__/settings.cpython-313.pyc differ diff --git a/novawow/__pycache__/urls.cpython-313.pyc b/novawow/__pycache__/urls.cpython-313.pyc index 14d4b53..41612c3 100644 Binary files a/novawow/__pycache__/urls.cpython-313.pyc and b/novawow/__pycache__/urls.cpython-313.pyc differ diff --git a/novawow/settings.py b/novawow/settings.py index 29a3463..7c47601 100644 --- a/novawow/settings.py +++ b/novawow/settings.py @@ -27,7 +27,6 @@ DEBUG = True ALLOWED_HOSTS = ['*'] - # Application definition INSTALLED_APPS = [