minecraft_manager/models.py

457 lines
14 KiB
Python

import json
import logging
import os
from datetime import datetime
from os.path import basename
import pytz
import yaml
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.db.models import Q
from django.db.models.signals import pre_delete
from django.dispatch import receiver
logger = logging.getLogger(__name__)
class MinecraftManagerUser(User):
class Meta:
proxy = True
permissions = (
('bots', 'Can use the bot control page'),
('chat', 'Can use chat page'),
)
class UserSettings(models.Model):
RESULT_OPTIONS = (
(10, '10'),
(25, '25'),
(50, '50'),
(100, '100'),
(-1, 'All')
)
THEME_OPTIONS = (
('DE', 'Default'),
('DA', 'Dark'),
('SO', 'Solar'),
('SL', 'Slate')
)
tzs = []
for tz in pytz.common_timezones:
tzs.append((tz, tz))
TIMEZONES = tuple(tzs)
auth_user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
default_results = models.SmallIntegerField("Default Results", default=10, choices=RESULT_OPTIONS)
default_theme = models.CharField("Theme", max_length=2, default='DE', choices=THEME_OPTIONS)
default_timezone = models.CharField("Timezone", max_length=50, default='UTC', choices=TIMEZONES)
search_player_ip = models.BooleanField("Include IP in Player search", default=False)
show_timestamp_chat = models.BooleanField("Show Timestamp By Chat", default=False)
last_ip = models.CharField(max_length=30, default="127.0.0.1", editable=False)
class Meta:
verbose_name = "User Settings"
verbose_name_plural = "User Settings"
def __str__(self):
return self.auth_user.username
def __repr__(self):
return self.auth_user.username
class Application(models.Model):
username = models.CharField("Minecraft Username", max_length=20, unique=True)
age = models.PositiveSmallIntegerField()
player_type = models.TextField("What's your favorite thing to do in Minecraft?", max_length=300)
ever_banned = models.BooleanField("Have you ever been banned?", default=False)
ever_banned_explanation = models.TextField("If you were previously banned, will you share why?", max_length=300, blank=True, null=True)
reference = models.CharField("How did you find out about us? Please give a website or player name, if applicable.", max_length=50, blank=True, null=True)
read_rules = models.CharField("Have you read the rules?", max_length=10)
accepted = models.BooleanField(null=True)
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
objects = models.Manager()
@property
def status(self):
if self.accepted is not None:
if self.accepted is True:
return "Accepted"
elif self.accepted is False:
return "Denied"
else:
return "Unknown"
else:
return "Unanswered"
@property
def date_display(self):
return str(self.date).split(".")[0]
def __str__(self):
return self.username
def __repr__(self):
return self.username
class Player(models.Model):
auth_user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True, blank=True)
uuid = models.CharField(max_length=36, unique=True)
username = models.CharField(max_length=20)
application = models.ForeignKey(Application, on_delete=models.SET_NULL, null=True, blank=True)
first_seen = models.DateField(null=True, blank=True)
last_seen = models.DateField(null=True, blank=True)
discord_id = models.CharField(max_length=30, unique=True, null=True, blank=True)
@property
def is_banned(self):
ban_file = os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json')
with open(ban_file, encoding='utf-8') as f:
bans = json.load(f)
if bans:
for ban in bans:
if self.uuid == ban['uuid']:
return True
return False
else:
return False
@property
def donor_status(self):
def format_time(timestamp):
return datetime.utcfromtimestamp(timestamp).strftime('%m/%d/%Y')
try:
from django_luckperms.models import LuckPermsPlayerPermissions as lppp
donator = lppp.objects.filter(permission='group.donator', uuid=self.uuid)
if donator.exists():
expiry = donator.first().expiry
if expiry > 0:
return "Until {}".format(format_time(expiry))
else:
return "Permanent"
else:
return "None"
except:
pass
try:
pex_file = os.path.join(getattr(settings, 'MINECRAFT_BASE_DIR', ''), 'plugins/PermissionsEx/permissions.yml')
with open(pex_file) as f:
yml = yaml.load(f)
for user in yml['users']:
if user == self.uuid:
if 'donator' in yml['users'][user]['group']:
u = yml['users'][user]
if 'group-donator-until' in u['options']:
unix_time = float(u['options']['group-donator-until'])
return 'Until {0}'.format(format_time(unix_time))
else:
return 'Permanent'
else:
return 'None'
except:
pass
return "N/A"
@property
def ips(self):
ips = []
if not getattr(self.auth_user, 'is_active', False):
query = IP.objects.filter(player=self)
for q in query:
ips.append(q.ip)
return " ".join(ips)
def __str__(self):
return "{} ({})".format(self.username, self.uuid)
class Ticket(models.Model):
PRIORITY = (
('L', 'Low'),
('M', 'Medium'),
('H', 'High')
)
WORLDS = (
('O', 'Overworld'),
('N', 'Nether'),
('E', 'The End')
)
player = models.ForeignKey(Player, related_name="ticket_player", on_delete=models.CASCADE, blank=True, null=True)
message = models.CharField(max_length=500)
priority = models.CharField(max_length=1, choices=PRIORITY, blank=True, default='L')
staff = models.ForeignKey(User, related_name="ticket_staff", on_delete=models.CASCADE, null=True, blank=True)
resolved = models.BooleanField(default=False)
world = models.CharField(max_length=1, choices=WORLDS, blank=True, null=True)
x = models.CharField(max_length=20, blank=True, null=True)
y = models.CharField(max_length=20, blank=True, null=True)
z = models.CharField(max_length=20, blank=True, null=True)
date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
objects = models.Manager()
@property
def location(self):
return "{0}, {1}, {2} in {3}".format(self.x, self.y, self.z, self.world_label)
@property
def world_label(self):
for W in self.WORLDS:
if W[0] == self.world:
return W[1]
return ""
@property
def resolved_label(self):
if self.resolved:
return "Resolved"
else:
return "Unresolved"
@property
def snippet(self):
if len(self.message) > 50:
return self.message[:50] + "..."
else:
return self.message
@property
def issuer(self):
if self.player:
pl = Player.objects.get(id=self.player_id)
return pl.username
else:
return "Unknown"
@property
def claimed_by(self):
if self.staff:
pl = User.objects.get(id=self.staff_id)
return pl.username
else:
return "Unclaimed"
@property
def is_claimed(self):
if self.staff:
return True
else:
return False
@property
def priority_display(self):
for P in self.PRIORITY:
if P[0] == self.priority:
return P[1]
return "Unknown"
@staticmethod
def priority_code_to_display(piority_code):
for P in Ticket.PRIORITY:
if P[0] == piority_code:
return P[1]
return "Unknown"
@property
def date_display(self):
return str(self.date).split(".")[0]
@property
def notes(self):
return TicketNote.objects.filter(ticket=self)
def __str__(self):
return "{}: {}".format(self.issuer, self.snippet)
class TicketNote(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
message = models.TextField(max_length=1000)
last_update = models.DateTimeField(auto_now_add=True, blank=True, null=True)
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
@property
def attachments(self):
return Attachment.objects.filter(ref_model=RefModels.TICKET_NOTE[0], ref_id=self.id)
class Meta:
verbose_name = "Ticket Note"
verbose_name_plural = "Ticket Notes"
def __str__(self):
return "Ticket: {0}".format(self.ticket.snippet)
class Note(models.Model):
IMPORTANCE = (
('L', 'Low'),
('M', 'Medium'),
('H', 'High')
)
player = models.ForeignKey(Player, related_name="note_player", on_delete=models.CASCADE)
message = models.CharField(max_length=200)
importance = models.CharField(max_length=1, choices=IMPORTANCE)
staff = models.ForeignKey(User, related_name="note_staff", on_delete=models.CASCADE, null=True, blank=True, limit_choices_to=Q(is_active=True))
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
@property
def snippet(self):
if len(self.message) > 50:
return self.message[:50] + "..."
else:
return self.message
@property
def importance_display(self):
for S in self.IMPORTANCE:
if S[0] == self.importance:
return S[1]
return "Unknown"
@staticmethod
def importance_code_to_display(importance_code):
for S in Note.IMPORTANCE:
if S[0] == importance_code:
return S[1]
return "Unknown"
@property
def issuer(self):
if self.staff:
pl = User.objects.get(id=self.staff_id)
return pl.username
else:
return "System"
@property
def issuee(self):
if self.player:
pl = Player.objects.get(id=self.player_id)
return pl.username
else:
return "Unknown"
@property
def date_display(self):
return str(self.date).split(".")[0]
@property
def attachments(self):
return Attachment.objects.filter(ref_model=RefModels.NOTE[0], ref_id=self.id)
def __str__(self):
return "{}: {}".format(self.issuee, self.snippet)
class RefModels:
TICKET_NOTE = 'T', 'Ticket Note'
NOTE = 'N', 'Note'
choices = TICKET_NOTE, NOTE
@staticmethod
def label(ref: str):
for c in RefModels.choices:
if c[0] == ref:
return c[1]
return "None"
def _attachment_upload_to(instance, filename):
return f"attachments/{instance.ref_name.lower().replace(' ', '_')}s/{instance.ref_id}/{filename}"
class Attachment(models.Model):
ref_model = models.CharField(max_length=1, choices=RefModels.choices)
ref_id = models.IntegerField()
file = models.FileField(upload_to=_attachment_upload_to)
created = models.DateTimeField(auto_now_add=True)
@property
def ref_name(self):
return RefModels.label(self.ref_model)
@property
def file_name(self):
return basename(self.file.name)
def __str__(self):
return self.file.name
@receiver(pre_delete, sender=Attachment, dispatch_uid="delete_attachments")
def attachment_delete(sender, instance, **kwargs):
instance.file.delete(False)
class IPManager(models.Manager):
def get_queryset(self):
users = User.objects.filter(is_active=True)
filtered = []
for user in users:
if getattr(user, 'player', False):
ips = IP.objects.filter(player=user.player)
for ip in ips:
filtered.append(ip.ip)
return super(IPManager, self).get_queryset().exclude(ip__in=filtered)
class IP(models.Model):
player = models.ForeignKey(Player, on_delete=models.CASCADE)
ip = models.CharField(max_length=30)
last_used = models.DateField(null=True, blank=True)
objects = models.Manager()
api = IPManager()
class Meta:
verbose_name = "IP"
verbose_name_plural = "IPs"
@property
def last_used_formatted(self):
if self.last_used:
return self.last_used
else:
return "N/A"
@property
def associated(self):
ips = IP.api.filter(ip=self.ip)
players = []
for ip in ips:
if self.player != ip.player:
players.append(ip.player)
if players:
return players
else:
return None
def __str__(self):
player = Player.objects.get(id=self.player_id)
return "{}: {}".format(player.username, self.ip)
class Alert(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
message = models.TextField(max_length=1000)
seen = models.BooleanField(default=False)
date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
@property
def snippet(self):
if len(self.message) > 50:
return self.message[:50] + "..."
else:
return self.message
def __str__(self):
return "Alert for {}".format(self.user.username)