#create your views here. # https://api.mojang.com/users/profiles/minecraft/ from __future__ import absolute_import import json, datetime, pytz, os, sys from django.utils import timezone from itertools import chain from django.http import JsonResponse from django.shortcuts import render, reverse, redirect from django.views.decorators.csrf import csrf_protect from django.utils.decorators import method_decorator from django.conf import settings from django.views.generic import View from django.contrib.auth.models import User from minecraft_manager.models import Application as AppModel, Player as PlayerModel, Ticket as TicketModel, Warning as WarningModel, IP as IPModel, Alert as AlertModel, Note as NoteModel, UserSettings as UserSettingsModel from minecraft_manager.forms import WarningForm, NoteForm import minecraft_manager.api.api as API import subprocess class Overview(View): def get(self, request): x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') if x_forwarded_for: user_ip = x_forwarded_for.split(',')[0] else: user_ip = request.META.get('REMOTE_ADDR') try: request.user.usersettings.last_ip = user_ip except: request.user.usersettings = UserSettingsModel(auth_user=request.user) request.user.usersettings.last_ip = user_ip request.user.usersettings.save() unanswered_apps = AppModel.unanswered.count() accepted_apps = AppModel.objects.filter(accepted=True).count() denied_apps = AppModel.objects.filter(accepted=False).count() unclaimed_tickets = TicketModel.unclaimed.count() claimed_tickets = TicketModel.claimed.count() resolved_tickets = TicketModel.objects.filter(resolved=True).count() counts = {'applications': AppModel.objects.count(), 'players': PlayerModel.objects.count(), "ips": IPModel.objects.count(), "tickets": TicketModel.objects.count(), "warnings": WarningModel.objects.count()} form = {'apps': {'unanswered': unanswered_apps, 'accepted': accepted_apps, 'denied': denied_apps}, 'tickets': {'unclaimed': unclaimed_tickets, 'claimed': claimed_tickets, 'resolved': resolved_tickets}, 'counts': counts} return render(request, 'minecraft_manager/overview.html', {'current_app': 'overview', 'form': form}) class CoreProtect(View): def get(self, request): #http://www.24carrotcraft.com/assets/cp/index.php?username=etzelia return render(request, 'minecraft_manager/coreprotect.html', {'current_app': 'coreprotect'}) class Activity(View): def get(self, request): #http://www.24carrotcraft.com/assets/cp/activity.php?username=etzelia return render(request, 'minecraft_manager/activity.html', {'current_app': 'activity'}) class Ban(View): def get(self, request): 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) for idx, ban in enumerate(bans): ban['source'] = API.strip_format(ban['source']) if PlayerModel.objects.filter(uuid=ban['uuid']).exists(): ban['id'] = PlayerModel.objects.get(uuid=ban['uuid']).id else: ban['id'] = 0 ban['source'] = API.strip_format(ban['source']) if ban['expires'] == "forever": ban['expires'] = "Permanent" else: dt = datetime.datetime.strptime(ban['expires'], "%Y-%m-%d %H:%M:%S %z") dt = dt.astimezone(pytz.timezone(request.user.usersettings.default_timezone)) ban['expires'] = dt.strftime("%m/%d/%y %I:%M %p") dt = datetime.datetime.strptime(ban['created'], "%Y-%m-%d %H:%M:%S %z") dt = dt.astimezone(pytz.timezone(request.user.usersettings.default_timezone)) ban['created'] = dt.strftime("%m/%d/%y %I:%M %p") bans[idx] = ban bans = sorted(bans, key=lambda ban: ban['created'], reverse=True) return render(request, 'minecraft_manager/ban.html', {'current_app': 'ban', 'bans': bans}) class Alert(View): def get(self, request): alerts = AlertModel.objects.filter(user=request.user).order_by('seen', '-id') unseen_alerts = AlertModel.objects.filter(user=request.user, seen=False) return render(request, 'minecraft_manager/alert.html', {'current_app': 'alert', 'alerts': alerts, 'unseen': len(unseen_alerts)}) def post(self, request): post = request.POST if 'action' in post: if post['action'] == 'mar': AlertModel.objects.filter(user=request.user).update(seen=True) alerts = AlertModel.objects.filter(user=request.user).order_by('seen', '-id') return render(request, 'minecraft_manager/alert.html', {'current_app': 'alert', 'alerts': alerts, 'unseen': 0}) class AlertInfo(View): def get(self, request, alert_id): alert = AlertModel.objects.get(id=alert_id) alert.seen = True alert.save() return render(request, 'minecraft_manager/alert_info.html', {'current_app': 'alert', 'alert': alert}) def post(self, request, alert_id): post = request.POST alert = AlertModel.objects.get(id=alert_id) if 'action' in post: if post['action'] == 'mu': alert.seen = False alert.save() return render(request, 'minecraft_manager/alert_info.html', {'current_app': 'alert', 'alert': alert}) class Application(View): def get(self, request): get = request.GET if 'accepted' in get: if get['accepted'].lower() == 'true': applications = AppModel.objects.filter(accepted=True) elif get['accepted'].lower() == 'false': applications = AppModel.objects.filter(accepted=False) else: applications = AppModel.objects.filter(accepted__isnull=True) else: applications1 = AppModel.objects.filter(accepted__isnull=True) applications2 = AppModel.objects.filter(accepted__isnull=False) applications = list(chain(applications1, applications2)) return render(request, 'minecraft_manager/application.html', {'current_app': 'application', 'applications': applications}) class Reference(View): def get(self, request): get = request.GET applications = AppModel.objects.all() return render(request, 'minecraft_manager/reference.html', {'current_app': 'application', 'applications': applications}) class ApplicationInfo(View): @method_decorator(csrf_protect) def get(self, request, application_id): application = AppModel.objects.get(id=application_id) return render(request, 'minecraft_manager/application_info.html', {'current_app': 'application', 'application': application}) def post(self, request, application_id): post = request.POST application = AppModel.objects.get(id=application_id) if post['accept']: if post['accept'] == 'accept': application.accepted = True if post['accept'] == 'deny': application.accepted = False application.save() API.plugin(post['accept'], application.username) API.discord_mcm("Application #**{0}** was **{1}** by **{2}**".format(application.id, "Accepted" if application.accepted else "Denied", request.user.player.username)) return render(request, 'minecraft_manager/application_info.html', {'current_app': 'application', 'application': application}) class Player(View): def get(self, request): players = PlayerModel.objects.all() ban_file = os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json') with open(ban_file, encoding='utf-8') as f: ban_list = json.load(f) bans = [] for ban in ban_list: bans.append(ban['uuid']) return render(request, 'minecraft_manager/player.html', {'current_app': 'player', 'players': players, 'bans': bans}) class PlayerInfo(View): def get(self, request, player_id): player = PlayerModel.objects.get(id=player_id) ips = IPModel.objects.filter(player=player) tickets = TicketModel.objects.filter(player=player) warnings = WarningModel.objects.filter(player=player) form = {'ips': ips, 'tickets': tickets, 'warnings': warnings} return render(request, 'minecraft_manager/player_info.html', {'current_app': 'player', 'player': player, 'form': form}) def post(self, request, player_id): player = PlayerModel.objects.get(id=player_id) return render(request, 'minecraft_manager/player_info.html', {'current_app': 'player', 'player': player}) class Ticket(View): def get(self, request): get = request.GET if 'claimed' in get: if get['claimed'].lower() == 'true': tickets = TicketModel.claimed.all() elif get['claimed'].lower() == 'false': tickets = TicketModel.unclaimed.all() else: tickets = TicketModel.objects.filter(resolved=True) else: tickets1 = TicketModel.objects.filter(resolved=False).order_by('-id') tickets2 = TicketModel.objects.filter(resolved=True).order_by('-id') tickets = list(chain(tickets1, tickets2)) return render(request, 'minecraft_manager/ticket.html', {'current_app': 'ticket', 'tickets': tickets}) class TicketInfo(View): @method_decorator(csrf_protect) def get(self, request, ticket_id): ticket = TicketModel.objects.get(id=ticket_id) active_staff = User.objects.filter(is_active=True) inactive_staff = User.objects.filter(is_active=False) notes = NoteModel.objects.filter(ref_id=ticket_id, ref_table='TI') note_form = NoteForm(instance=request.user) has_note = False for note in notes: if note.author == request.user: note_form = NoteForm(instance=note) has_note = True if not has_note: note_form.fields['ref_table'].initial = 'TI' note_form.fields['ref_id'].initial = ticket_id form = {'active_staff': active_staff, 'inactive_staff': inactive_staff, 'priority': TicketModel.PRIORITY, 'resolved': ticket.resolved, 'note_form': note_form.as_p(), 'notes': notes, 'has_note': has_note, 'show_note': False} return render(request, 'minecraft_manager/ticket_info.html', {'current_app': 'ticket', 'ticket': ticket, 'form': form}) def post(self, request, ticket_id): post = request.POST ticket = TicketModel.objects.get(id=ticket_id) if 'priority' in post: if post['priority'] != ticket.priority: API.discord_mcm( "Ticket #**{0}**'s priority was changed from **{1}** to **{2}** by **{3}**".format(ticket.id, ticket.priority_display, TicketModel.priority_code_to_display( post['priority']), request.user.username)) ticket.priority = post['priority'] if 'staff' in post and 'resolved' not in post: if post['staff']: staff = User.objects.get(id=post['staff']) if post['staff'] != str(getattr(ticket.staff, 'id', '-1')): if post['staff'] == str(request.user.id): API.discord_mcm( "Ticket #**{0}** was claimed by **{1}**".format(ticket.id, request.user.username)) else: API.discord_mcm( "Ticket #**{0}** was given to **{1}** by **{2}**".format(ticket.id, staff.username, request.user.username)) else: staff = None API.discord_mcm( "Ticket #**{0}** was unclaimed by **{1}**".format(ticket.id, request.user.username)) ticket.staff = staff if 'resolved' in post: API.discord_mcm("Ticket #**{0}** was resolved by **{1}**".format(ticket.id, request.user.username)) ticket.resolved = True ticket.save() show_note = False if 'note' in post: note_form = NoteForm(post) if note_form.is_valid(): n = note_form.save(commit=False) if post['note'] == 'create': n.author = request.user n.save() elif post['note'] == 'edit': db = NoteModel.objects.get(ref_id=ticket_id, ref_table='TI', author=request.user) db.message = n.message db.last_update = timezone.now() db.save() else: show_note = True else: note_form = NoteForm(instance=request.user) notes = NoteModel.objects.filter(ref_id=ticket_id, ref_table='TI') has_note = False for note in notes: if note.author == request.user: note_form = NoteForm(instance=note) has_note = True if not has_note: note_form.fields['ref_table'].initial = 'TI' note_form.fields['ref_id'].initial = ticket_id active_staff = User.objects.filter(is_active=True) inactive_staff = User.objects.filter(is_active=False) form = {'active_staff': active_staff, 'inactive_staff': inactive_staff, 'priority': TicketModel.PRIORITY, 'resolved': ticket.resolved, 'note_form': note_form.as_p(), 'notes': notes, 'has_note': has_note, 'show_note': show_note} return render(request, 'minecraft_manager/ticket_info.html', {'current_app': 'ticket', 'ticket': ticket, 'form': form}) class Warning(View): def get(self, request): warnings = WarningModel.objects.order_by('-id') return render(request, 'minecraft_manager/warning.html', {'current_app': 'warning', 'warnings': warnings}) class WarningInfo(View): @method_decorator(csrf_protect) def get(self, request, warning_id): warning = WarningModel.objects.get(id=warning_id) form = {'severity': WarningModel.SEVERITY} return render(request, 'minecraft_manager/warning_info.html', {'current_app': 'warning', 'form': form, 'warning': warning}) def post(self, request, warning_id): post = request.POST warning = WarningModel.objects.get(id=warning_id) if 'severity' in post: API.discord_mcm("Warning #**{0}**'s severity was changed from {1} to {2} by {3}".format(warning.id, warning.severity_display, WarningModel.severity_code_to_display( post[ 'severity']), request.user.player.username)) warning.severity = post['severity'] warning.save() form = {'severity': WarningModel.SEVERITY} return render(request, 'minecraft_manager/warning_info.html', {'current_app': 'warning', 'form': form, 'warning': warning}) class WarningAdd(View): @method_decorator(csrf_protect) def get(self, request): form = WarningForm().as_p() return render(request, 'minecraft_manager/warning_add.html', {'current_app': 'warning', 'form': form}) def post(self, request): post = request.POST form = WarningForm(post) if form.is_valid(): warning = form.save() warning.staff = request.user warning.save() API.discord_mcm( "**{0}** issued a **{1}** severity warning to **{2}**\nPreview: {3}".format(warning.staff.player.username, warning.severity_display, warning.player.username, warning.snippet)) return redirect("{0}{1}".format(reverse('warning'), warning.id)) else: return render(request, 'minecraft_manager/warning_add.html', {'current_app': 'warning', 'form': form}) class Report(View): def get(self, request): get = request.GET results = [] timestamp = None report = get.get('report') world = get.get('world') if report and world: # Get some results report_dir = os.path.join(settings.MINECRAFT_BASE_DIR, 'plugins', 'MinecraftManager', 'report.json') if os.path.exists(report_dir): with open(report_dir) as rpt: raw = json.load(rpt) time = raw.get('time') timestamp = datetime.datetime.utcfromtimestamp(float(time)) timestamp = pytz.timezone('UTC').localize(timestamp) timestamp = timestamp.astimezone(pytz.timezone(request.user.usersettings.default_timezone)) for w in raw: if w.endswith('_nether') and world == 'nether': world_raw = raw[w] elif w.endswith('_the_end') and world == 'end': world_raw = raw[w] elif w != 'time' and not (w.endswith('_nether') or w.endswith('_the_end')) and world == 'overworld': # Need to think of a way to clean this up... world_raw = raw[w] break results_raw = world_raw.get(report) for result in results_raw: if report == 'players': results.append({'name': result.get('name'), 'x': result.get('x'), 'y': result.get('y'), 'z': result.get('z')}) else: type_formatted = ' '.join([part.title() for part in result.split('_')]) if report == 'entities': for location in results_raw[result]: results.append({'name': type_formatted, 'x': location.get('x'), 'y': location.get('y'), 'z': location.get('z')}) else: results.append({'name': type_formatted, 'count': results_raw.get(result)}) else: results = 'NONE' elif report or world: results = 'BOTH' if len(results) == 0: results = 'EMPTY' return render(request, 'minecraft_manager/report.html', {'current_app': 'report', 'report': report, 'world': world, 'timestamp': timestamp, 'results': results}) def post(self, request): pass class Chat(View): @staticmethod def replace_ascii(message): return message.replace(" ", "\\040").replace("\"", "\\042").replace("#", "\\043").replace("$", "\\044")\ .replace("%", "\\045").replace("&", "\\046").replace("(", "\\050").replace(")", "\\051").replace("*", "\\052")\ .replace("+", "\\053").replace("-", "\\055") def get(self, request): staff = hasattr(settings, 'STAFF_LOG') return render(request, 'minecraft_manager/chat.html', {'current_app': 'chat', 'staff': staff}) def post(self, request): post = request.POST if 'chat' in post and 'message' in post: API.plugin(post['chat'], "{0} {1}".format(request.user.player.username, post['message'])) data = {'success': True, 'message': 'Message sent successfully.'} else: data = {'success': False, 'message': 'No chat type or message set.'} return JsonResponse(data) class Bots(View): def assets(self): path = os.path.abspath(os.path.dirname(__file__)) bot_dir = os.path.join(path, 'assets/bots') return bot_dir def get_bots(self): bot_dir = getattr(settings, 'BOT_DIR', None) bots = [] if bot_dir: for file in os.listdir(bot_dir): if file.endswith('.bot.py'): ve = file.replace('.bot.py', '') py = os.path.join(bot_dir, ve, 'bin/python') if os.path.isfile(py): bots.append({'name': file.replace('.bot.py', ''), 'asset': False, 'executable': py}) else: bots.append({'name': file.replace('.bot.py', ''), 'asset': False, 'executable': sys.executable}) # Also get packaged MCM bots for file in os.listdir(self.assets()): if file.endswith('.bot.py'): bots.append({'name': file.replace('.bot.py', ''), 'asset': True, 'executable': sys.executable}) return bots def get_form(self): bots = self.get_bots() plugin_port = getattr(settings, 'PLUGIN_PORT', None) screens = subprocess.getoutput("screen -ls") form = {'screens': screens, 'bots': bots} for bot in bots: form[bot['name']] = True if "{0}_{1}".format(plugin_port, bot['name']) in screens else False return form def get(self, request): return render(request, 'minecraft_manager/bots.html', {'current_app': 'bots', 'form': self.get_form()}) def post(self, request): post = request.POST plugin_port = getattr(settings, 'PLUGIN_PORT', None) for bot in self.get_bots(): if bot['name'] in post: if post[bot['name']] == "stop": subprocess.run('screen -X -S "{0}_{1}" quit'.format(plugin_port, bot['name']), shell=True) elif post[bot['name']] in ('start', 'restart'): subprocess.run('screen -X -S "{0}_{1}" quit'.format(plugin_port, bot['name']), shell=True) path = self.assets() if bot['asset'] else getattr(settings, 'BOT_DIR', "") if not path.endswith("/"): path += "/" print('screen -S {0}_{1} -d -m {2} {3}{1}.bot.py'.format(plugin_port, bot['name'], bot['executable'], path)) subprocess.run( 'screen -S {0}_{1} -d -m {2} {3}{1}.bot.py'.format(plugin_port, bot['name'], bot['executable'], path), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) return render(request, 'minecraft_manager/bots.html', {'current_app': 'bots', 'form': self.get_form()})