2026-03-13 00:31:17 -04:00
|
|
|
import uuid
|
|
|
|
|
|
2026-03-13 17:31:52 -04:00
|
|
|
from datetime import timedelta
|
2026-03-12 15:05:02 -04:00
|
|
|
from django.db import models
|
2026-03-13 00:31:17 -04:00
|
|
|
from django.db.models.signals import post_save
|
|
|
|
|
from django.dispatch import receiver
|
|
|
|
|
from django.conf import settings
|
2026-03-13 17:31:52 -04:00
|
|
|
from django.utils import timezone
|
|
|
|
|
|
|
|
|
|
from apps.lyric.models import Token
|
2026-03-13 00:31:17 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Room(models.Model):
|
|
|
|
|
GATHERING = "GATHERING"
|
|
|
|
|
OPEN = "OPEN"
|
|
|
|
|
RENEWAL_DUE = "RENEWAL_DUE"
|
|
|
|
|
GATE_STATUS_CHOICES = [
|
2026-03-14 01:14:05 -04:00
|
|
|
(GATHERING, "GATHERING GAMERS"),
|
2026-03-13 00:31:17 -04:00
|
|
|
(OPEN, "Open"),
|
|
|
|
|
(RENEWAL_DUE, "Renewal Due"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
PRIVATE = "PRIVATE"
|
|
|
|
|
PUBLIC = "PUBLIC"
|
|
|
|
|
INVITE_ONLY = "INVITE ONLY"
|
|
|
|
|
VISIBILITY_CHOICES = [
|
|
|
|
|
(PRIVATE, "Private"),
|
|
|
|
|
(PUBLIC, "Public"),
|
|
|
|
|
(INVITE_ONLY, "Invite Only"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
|
|
|
name = models.CharField(max_length=200)
|
|
|
|
|
owner = models.ForeignKey(
|
|
|
|
|
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="owned_rooms"
|
|
|
|
|
)
|
|
|
|
|
visibility = models.CharField(max_length=20, choices=VISIBILITY_CHOICES, default=PRIVATE)
|
|
|
|
|
gate_status = models.CharField(max_length=20, choices=GATE_STATUS_CHOICES, default=GATHERING)
|
2026-03-13 17:31:52 -04:00
|
|
|
renewal_period = models.DurationField(null=True, blank=True, default=timedelta(days=7))
|
2026-03-13 00:31:17 -04:00
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
|
board_state = models.JSONField(default=dict)
|
|
|
|
|
seed_count = models.IntegerField(default=12)
|
|
|
|
|
|
2026-03-13 18:37:19 -04:00
|
|
|
|
2026-03-13 00:31:17 -04:00
|
|
|
class GateSlot(models.Model):
|
|
|
|
|
EMPTY = "EMPTY"
|
|
|
|
|
RESERVED = "RESERVED"
|
|
|
|
|
FILLED = "FILLED"
|
|
|
|
|
STATUS_CHOICES = [
|
|
|
|
|
(EMPTY, "Empty"),
|
|
|
|
|
(RESERVED, "Reserved"),
|
|
|
|
|
(FILLED, "Filled"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
room = models.ForeignKey(Room, on_delete=models.CASCADE, related_name="gate_slots")
|
|
|
|
|
slot_number = models.IntegerField()
|
|
|
|
|
gamer = models.ForeignKey(
|
|
|
|
|
settings.AUTH_USER_MODEL, null=True, blank=True,
|
|
|
|
|
on_delete=models.SET_NULL, related_name="gate_slots"
|
|
|
|
|
)
|
|
|
|
|
funded_by = models.ForeignKey(
|
|
|
|
|
settings.AUTH_USER_MODEL, null=True, blank=True,
|
|
|
|
|
on_delete=models.SET_NULL, related_name="funded_slots"
|
|
|
|
|
)
|
|
|
|
|
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=EMPTY)
|
|
|
|
|
reserved_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
|
filled_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
|
|
2026-03-12 15:05:02 -04:00
|
|
|
|
2026-03-13 18:37:19 -04:00
|
|
|
class RoomInvite(models.Model):
|
|
|
|
|
PENDING = "PENDING"
|
|
|
|
|
ACCEPTED = "ACCEPTED"
|
|
|
|
|
DECLINED = "DECLINED"
|
|
|
|
|
STATUS_CHOICES = [
|
|
|
|
|
(PENDING, "Pending"),
|
|
|
|
|
(ACCEPTED, "Accepted"),
|
|
|
|
|
(DECLINED, "Declined"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
room = models.ForeignKey(Room, on_delete=models.CASCADE, related_name="invites")
|
|
|
|
|
inviter = models.ForeignKey(
|
|
|
|
|
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="sent_invites"
|
|
|
|
|
)
|
|
|
|
|
invitee_email = models.EmailField()
|
|
|
|
|
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default=PENDING)
|
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
|
|
|
|
|
|
|
|
|
2026-03-13 00:31:17 -04:00
|
|
|
@receiver(post_save, sender=Room)
|
|
|
|
|
def create_gate_slots(sender, instance, created, **kwargs):
|
|
|
|
|
if created:
|
|
|
|
|
for i in range(1, 7):
|
|
|
|
|
GateSlot.objects.create(room=instance, slot_number=i)
|
2026-03-13 17:31:52 -04:00
|
|
|
|
|
|
|
|
|
2026-03-14 22:00:16 -04:00
|
|
|
def select_token(user):
|
|
|
|
|
if user.is_staff:
|
|
|
|
|
pass_token = user.tokens.filter(token_type=Token.PASS).first()
|
|
|
|
|
if pass_token:
|
|
|
|
|
return pass_token
|
|
|
|
|
coin = user.tokens.filter(token_type=Token.COIN, current_room__isnull=True).first()
|
|
|
|
|
if coin:
|
|
|
|
|
return coin
|
|
|
|
|
free = user.tokens.filter(
|
|
|
|
|
token_type=Token.FREE,
|
|
|
|
|
expires_at__gt=timezone.now(),
|
|
|
|
|
).order_by("expires_at").first()
|
|
|
|
|
if free:
|
|
|
|
|
return free
|
|
|
|
|
return user.tokens.filter(token_type=Token.TITHE).first()
|
|
|
|
|
|
|
|
|
|
|
2026-03-13 17:31:52 -04:00
|
|
|
def debit_token(user, slot, token):
|
|
|
|
|
if token.token_type == Token.COIN:
|
|
|
|
|
token.current_room = slot.room
|
2026-03-13 22:51:42 -04:00
|
|
|
period = slot.room.renewal_period or timedelta(days=7)
|
|
|
|
|
token.next_ready_at = timezone.now() + period
|
2026-03-13 17:31:52 -04:00
|
|
|
token.save()
|
2026-03-14 22:00:16 -04:00
|
|
|
elif token.token_type != Token.PASS:
|
2026-03-13 17:31:52 -04:00
|
|
|
token.delete()
|
|
|
|
|
slot.gamer = user
|
|
|
|
|
slot.status = GateSlot.FILLED
|
|
|
|
|
slot.filled_at = timezone.now()
|
|
|
|
|
slot.save()
|
2026-03-13 22:51:42 -04:00
|
|
|
|
|
|
|
|
room = slot.room
|
|
|
|
|
if not room.gate_slots.filter(status=GateSlot.EMPTY).exists():
|
|
|
|
|
room.gate_status = Room.OPEN
|
|
|
|
|
room.save()
|