b3184fbdfe
El módulo monolítico (~3200 líneas) se convierte en un paquete cuyo __init__.py re-exporta todas las vistas, de modo que `views.X`, `from home.views import X` y la cadena 'home.views.not_found' (handler404) siguen funcionando sin tocar urls.py. Módulos por sección: _base (imports comunes + require_paid_stripe), auth, pages, account, recruit, characters, store, points, guild, history, players y payments. Cada submódulo hereda los imports con `from ._base import *`; las dependencias entre secciones se resuelven con imports explícitos (guild->auth, recruit/history->account). Se eliminan dos definiciones duplicadas que quedaban muertas al cargar: - get_character_image (primera copia, idéntica a la efectiva) - store_novawow_success_view (stub sin SOAP, pisado por la versión real) Validado con `manage.py check` (0 issues), resolución de las 100 rutas y un chequeo estático de nombres no definidos por módulo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
162 lines
7.0 KiB
Python
162 lines
7.0 KiB
Python
from ._base import *
|
|
from .account import get_account_status, get_account_characters
|
|
|
|
|
|
def recruit_a_friend_view(request):
|
|
username = request.session.get('username')
|
|
account_status = None
|
|
characters = []
|
|
claimed_rewards_count = 0
|
|
|
|
# Si el usuario está logueado, obtener su información
|
|
if username:
|
|
# Obtener información del usuario
|
|
with connections['acore_auth'].cursor() as cursor:
|
|
cursor.execute("SELECT id, last_ip FROM account WHERE username = %s", [username])
|
|
account_data = cursor.fetchone()
|
|
|
|
if account_data:
|
|
account_id = account_data[0]
|
|
user_ip = account_data[1]
|
|
account_status = get_account_status(account_id)
|
|
characters = get_account_characters(account_id)
|
|
claimed_rewards_count = ClaimedReward.objects.filter(account_id=account_id).count()
|
|
else:
|
|
account_id = None
|
|
user_ip = None
|
|
else:
|
|
account_id = None
|
|
user_ip = None
|
|
|
|
# Obtener todas las recompensas
|
|
all_rewards = RecruitReward.objects.all()
|
|
available_rewards = []
|
|
|
|
# Si el usuario está logueado, filtrar recompensas disponibles
|
|
if account_id:
|
|
available_rewards = [reward for reward in all_rewards if not ClaimedReward.objects.filter(account_id=account_id, recruit_reward=reward).exists()]
|
|
|
|
# Manejar solicitudes POST (solo si el usuario está logueado)
|
|
if request.method == 'POST':
|
|
if not username:
|
|
return JsonResponse({'success': False, 'message': 'Debes estar logueado para reclamar una recompensa.'})
|
|
|
|
if not account_id or account_status is None:
|
|
return JsonResponse({'success': False, 'message': 'No se ha encontrado tu cuenta.'})
|
|
|
|
reward_id = request.POST.get('reward_id')
|
|
character_name = request.POST.get('character')
|
|
|
|
if not reward_id or not character_name:
|
|
return JsonResponse({'success': False, 'message': 'Selecciona una recompensa y un personaje.'})
|
|
|
|
try:
|
|
reward = RecruitReward.objects.get(id=reward_id)
|
|
|
|
# Verificar si ya se ha reclamado la recompensa
|
|
if ClaimedReward.objects.filter(account_id=account_id, recruit_reward=reward).exists():
|
|
return JsonResponse({'success': False, 'message': 'Ya has reclamado esta recompensa.'})
|
|
|
|
# Verificar si cumple los requisitos
|
|
if account_status['recruited_level_80_count'] < reward.required_friends:
|
|
return JsonResponse({'success': False, 'message': 'No cumples con los requisitos.'})
|
|
|
|
# Obtener amigos reclutados que han alcanzado el nivel 80
|
|
recruited_friends = get_recruited_friends_at_level_80(account_id, reward.required_friends)
|
|
|
|
# Comprobar si alguno de los reclutados es válido
|
|
if len(recruited_friends) < reward.required_friends:
|
|
return JsonResponse({'success': False, 'message': 'No cumples con los requisitos o los amigos tienen la misma IP.'})
|
|
|
|
# Comprobar si alguno de los reclutados tiene la misma IP que el usuario
|
|
if check_recruited_ip_conflict(recruited_friends, user_ip):
|
|
return JsonResponse({'success': False, 'message': 'No puedes reclamar recompensas de amigos que tienen la misma IP.'})
|
|
|
|
# Ejecutar el comando SOAP
|
|
command = f".send items {character_name} 'Recompensa de Recluta Amigo' 'Tu amigo alcanzó el Nivel 80' {reward.item_id}:{reward.item_quantity}"
|
|
response = execute_soap_command(command)
|
|
|
|
if response and "Mail sent to" in response:
|
|
# Registrar la recompensa como reclamada
|
|
ClaimedReward.objects.create(
|
|
account_id=account_id,
|
|
username=username,
|
|
recruit_reward=reward,
|
|
character_name=character_name,
|
|
claimed_at=timezone.now(),
|
|
ip_address=request.META.get('REMOTE_ADDR')
|
|
)
|
|
claimed_rewards_count = ClaimedReward.objects.filter(account_id=account_id).count()
|
|
|
|
return JsonResponse({
|
|
'success': True,
|
|
'message': f"Recompensa '{reward.reward_name}' entregada a {character_name}.",
|
|
'claimed_count': claimed_rewards_count
|
|
})
|
|
else:
|
|
return JsonResponse({'success': False, 'message': 'Error al entregar la recompensa.'})
|
|
|
|
except RecruitReward.DoesNotExist:
|
|
return JsonResponse({'success': False, 'message': 'Recompensa no encontrada.'})
|
|
|
|
# Renderizar la página para usuarios no logueados y logueados
|
|
return render(request, 'recruit/recruit_a_friend.html', {
|
|
'account_status': account_status,
|
|
'all_rewards': all_rewards,
|
|
'available_rewards': available_rewards,
|
|
'characters': characters,
|
|
'claimed_rewards_count': claimed_rewards_count,
|
|
'is_logged_in': username is not None
|
|
})
|
|
def check_recruited_ip_conflict(recruited_accounts, user_ip):
|
|
"""
|
|
Verifica si alguna de las cuentas reclutadas tiene la misma IP que la cuenta del reclutador.
|
|
"""
|
|
if not recruited_accounts:
|
|
return False
|
|
|
|
with connections['acore_auth'].cursor() as cursor:
|
|
cursor.execute("""
|
|
SELECT last_ip FROM account WHERE id IN %s
|
|
""", [tuple(recruited_accounts)])
|
|
recruited_ips = [ip[0] for ip in cursor.fetchall()]
|
|
|
|
return user_ip in recruited_ips
|
|
def get_recruited_friends_at_level_80(account_id, required_count):
|
|
"""
|
|
Obtiene una lista de IDs de amigos reclutados que han alcanzado el nivel 80 y
|
|
no tienen la misma IP que el reclutador.
|
|
"""
|
|
# Primero obtener los IDs de cuentas reclutadas desde `acore_auth`
|
|
with connections['acore_auth'].cursor() as auth_cursor:
|
|
auth_cursor.execute("""
|
|
SELECT id, last_ip FROM account WHERE recruiter = %s
|
|
""", [account_id])
|
|
recruited_accounts = auth_cursor.fetchall()
|
|
|
|
if not recruited_accounts:
|
|
return []
|
|
|
|
# Extraer IDs y IPs de las cuentas reclutadas
|
|
recruited_ids = [acc[0] for acc in recruited_accounts]
|
|
recruited_ips = {acc[0]: acc[1] for acc in recruited_accounts}
|
|
|
|
# Obtener la IP del usuario actual
|
|
with connections['acore_auth'].cursor() as cursor:
|
|
cursor.execute("SELECT last_ip FROM account WHERE id = %s", [account_id])
|
|
user_ip = cursor.fetchone()[0]
|
|
|
|
# Luego, verificar cuáles de esas cuentas tienen personajes al nivel 80 en `acore_characters`
|
|
with connections['acore_characters'].cursor() as char_cursor:
|
|
char_cursor.execute("""
|
|
SELECT DISTINCT account FROM characters
|
|
WHERE level = 80 AND account IN %s
|
|
LIMIT %s
|
|
""", [tuple(recruited_ids), required_count])
|
|
recruited_at_level_80 = [acc[0] for acc in char_cursor.fetchall()]
|
|
|
|
# Filtrar cuentas que no tienen la misma IP que el usuario actual
|
|
valid_recruits = [acc for acc in recruited_at_level_80 if recruited_ips[acc] != user_ip]
|
|
|
|
return valid_recruits
|