Merge branch 'captcha' of birbmc.com:BirbMC/minecraft_manager

# Conflicts:
#	models.py
#	overview.py
roll
Etzelia 2021-03-15 21:09:53 -05:00
commit dead183dad
No known key found for this signature in database
GPG Key ID: 708511AE7ABC5314
27 changed files with 618 additions and 513 deletions

View File

@ -22,48 +22,47 @@ PLUGIN_DEMOTE = 'demote'
def plugin(key, command):
host = '127.0.0.1'
port = getattr(settings, 'PLUGIN_PORT', None)
full_command = "{0} {1}".format(key, command)
if port and plugin_exists():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
sock.sendall(full_command.encode('utf-8'))
sock.close()
try:
host = '127.0.0.1'
port = getattr(settings, 'PLUGIN_PORT', None)
full_command = "{0} {1}".format(key, command)
if port and plugin_exists():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
sock.sendall(full_command.encode('utf-8'))
sock.close()
return True
except:
pass
return False
def discord_mcm(message='', embeds=None, ping=False):
def discord_mcm(message='', embed=None, ping=False):
discord_mcm_webhook = getattr(settings, 'DISCORD_MCM_WEBHOOK', None)
if discord_mcm_webhook:
ping_list = getattr(settings, 'DISCORD_PING_LIST', [])
if ping and ping_list:
ping_list = ["<@&{0}>".format(ping) for ping in ping_list]
message = "{0}\n{1}".format(" ".join(ping_list), message)
data = {}
if message:
data['content'] = message
if embeds:
data['embeds'] = embeds
return requests.post(discord_mcm_webhook, json=data)
return post_webhook(discord_mcm_webhook, message, embed, ping)
return None
def discord_notification(message='', embeds=None, ping=False):
def discord_notification(message='', embed=None, ping=False):
discord_notification_webhook = getattr(settings, 'DISCORD_NOTIFICATION_WEBHOOK', None)
if discord_notification_webhook:
ping_list = getattr(settings, 'DISCORD_PING_LIST', [])
if ping and ping_list:
ping_list = ["<@&{0}>".format(ping) for ping in ping_list]
message = "{0}\n{1}".format(" ".join(ping_list), message)
data = {}
if message:
data['content'] = message
if embeds:
data['embeds'] = embeds
return requests.post(discord_notification_webhook, json=data)
return post_webhook(discord_notification_webhook, message, embed, ping)
return None
def post_webhook(webhook_url, message, embed, ping):
ping_list = getattr(settings, 'DISCORD_PING_LIST', [])
if ping and ping_list:
ping_list = ["<@&{0}>".format(ping) for ping in ping_list]
message = "{0}\n{1}".format(" ".join(ping_list), message)
data = {}
if message:
data['content'] = message
if embed:
data['embeds'] = [embed.to_dict()]
return requests.post(webhook_url, json=data)
def strip_format(message):
return message.replace("§0", "").replace("§1", "").replace("§2", "").replace("§3", "").replace("§4", "") \

View File

@ -1,212 +0,0 @@
import discord, logging, re, sys, traceback, asyncio
from minecraft_manager.models import Application, Player
from minecraft_manager.api import api
from django.contrib.auth.models import User
from django.conf import settings
from django.db import close_old_connections
logger = logging.getLogger(__name__)
class Discord(discord.Client):
discord_game = 'MCM'
prefix = getattr(settings, 'DISCORD_BOT_PREFIX', '!')
auth_roles = getattr(settings, 'DISCORD_BOT_ROLES', [])
error_users = getattr(settings, 'DISCORD_ERROR_USERS', [])
new_member_roles = getattr(settings, 'DISCORD_BOT_NEW_MEMBER_ROLES', [])
token = None
def __init__(self, token, **kwargs):
super().__init__(**kwargs)
self.token = token
@asyncio.coroutine
def on_ready(self):
print('Logged in as')
print(self.user.name)
print(self.user.id)
print(discord.__version__)
print('Voice Loaded: {0}'.format(discord.opus.is_loaded()))
print('OAuth URL: https://discordapp.com/api/oauth2/authorize?client_id={0}&permissions=0&scope=bot'.format(self.user.id))
print('------')
logger.info('Logged in as {0} ({1}) with discord.py v{2}'.format(self.user.name, self.user.id, discord.__version__))
yield from self.change_presence(game=discord.Game(name=self.discord_game))
@asyncio.coroutine
def discord_message(self, channel, message):
if isinstance(message, discord.Embed):
for idx, field in enumerate(message.fields):
if not field.value:
message.set_field_at(idx, name=field.name, value="N/A")
yield from self.send_message(channel, embed=message)
else:
yield from self.send_message(channel, message)
@asyncio.coroutine
def on_message(self, message):
# IGNORE DM AND BOTS
if message.author.bot is True or message.channel.is_private is True:
return
member_roles = [role.id for role in message.author.roles]
# FIX STALE DB CONNECTIONS
close_old_connections()
# IF MEMBER IS NOT AUTHORIZED, IGNORE
if not any(role in self.auth_roles for role in member_roles):
return
# HELP
match = re.match("[{0}]help$".format(self.prefix), message.content)
if match:
embed = discord.Embed(colour=discord.Colour(0x417505))
embed.set_thumbnail(url="https://cdn.discordapp.com/avatars/454457830918062081/b5792489bc43d9e17b8f657880a17dd4.png")
embed.add_field(name="Minecraft Manager Help", value="-----------------------------")
embed.add_field(name="{}[app ]search <username>".format(self.prefix), value="Search for applications by partial or exact username.")
embed.add_field(name="{}[app ]info <app ID>".format(self.prefix), value="Get detailed information about a specific application.")
embed.add_field(name="{}[app ]accept|deny <app ID>".format(self.prefix), value="Take action on an application.")
embed.add_field(name="{}demote <username>".format(self.prefix), value="Demote a player to the role given to accepted applications.")
embed.add_field(name="{}compare".format(self.prefix), value="Compare Discord users to the Whitelist.")
yield from self.discord_message(message.channel, embed)
# APP COMMANDS WITH APP ID
match = re.match("[{0}](?:app )?(i|info|a|accept|d|deny) (\d+)$".format(self.prefix), message.content)
if match:
if match.group(1) and match.group(2):
action = match.group(1)[:1]
action_display = "accept" if action == "a" else "deny" if action == "d" else ""
application = None
try:
application = Application.objects.get(id=match.group(2))
except:
yield from self.discord_message(message.channel, "An Application with that ID doesn't exist.")
return
if action == "i":
# Info
msg = self.build_info(application)
else:
# Action
accept = True if action == "a" else False
if not application.accepted:
application.accepted = accept
application.save()
if Player.objects.filter(username__iexact=application.username).exists():
player = Player.objects.get(username__iexact=application.username)
player.application_id = application.id
player.save()
msg = "App ID **{0}** was successfully {1}.".format(match.group(2), "accepted" if accept else "denied")
api.plugin("accept" if accept else "deny", application.username)
else:
msg = "App ID **{0}** was already {1}.".format(match.group(2), "accepted" if application.accepted else "denied")
yield from self.discord_message(message.channel, msg)
return
# APP INFO WITH PARTIAL NAME SEARCH
match = re.match("[{0}](?:app )?(?:search|info) (\S+)?$".format(self.prefix), message.content)
if match:
search = match.group(1)
applications = Application.objects.filter(username__icontains=search)[:10]
count = Application.objects.filter(username__icontains=search).count()
if count > 0:
if count == 1:
info = self.build_info(applications[0])
else:
info = "**Found the following applications**"
for app in applications:
info += "\n{0} - {1} ({2})".format(app.id, app.username.replace("_", "\\_"), app.status)
if count > 10:
info += "\n**This is only 10 applications out of {0} found. Please narrow your search if possible.**".format(
len(applications))
else:
players = Player.objects.filter(username__icontains=search, application__isnull=False)[:10]
count = Player.objects.filter(username__icontains=search, application__isnull=False).count()
if count > 0:
if count == 1:
yield from self.discord_message(message.channel, "**No applications matched, however there is a player match**")
info = self.build_info(players[0].application)
else:
info = "**No applications matched, however there are player matches**"
for player in players:
app = player.application
info += "\n{0} - {1} AKA {2} ({3})".format(app.id, app.username.replace("_", "\\_"), player.username.replace("_", "\\_"), app.status)
if count > 10:
info += "\n**This is only 10 players out of {0} found. Please narrow your search if possible.**".format(
len(players))
else:
info = "No applications matched that search."
yield from self.discord_message(message.channel, info)
# DEMOTE A PLAYER TO MEMBER
match = re.match("[{0}]demote (\w+)$".format(self.prefix), message.content)
if match:
yield from self.delete_message(message)
username = match.group(1)
api.plugin(api.PLUGIN_DEMOTE, username)
deactivated = ""
if User.objects.filter(username__iexact=username).exists():
user = User.objects.get(username__iexact=username)
user.is_active = False
user.save()
deactivated = " and de-activated"
yield from self.discord_message(message.channel, "{} has been demoted{}.".format(username, deactivated))
# COMPARE DISCORD USERS TO WHITELIST
match = re.match("[{0}]compare".format(self.prefix), message.content)
if match:
yield from self.delete_message(message)
yield from self.send_typing(message.channel)
no_player = []
no_application = []
for member in message.server.members:
if member.bot:
continue
name = member.nick if member.nick else member.name
try:
Player.objects.get(username__iexact=name)
except:
no_player.append(name)
try:
Application.objects.get(username__iexact=name)
except:
no_player = no_player[:-1]
no_application.append(name)
header = "**The following users have an application match, but no player match on the whitelist:**\n"
yield from self.discord_message(message.author, "{}```{}```".format(header, "\n".join(no_player)))
header = "**The following users do not have an application or player match on the whitelist:**\n"
yield from self.discord_message(message.author, "{}```{}```".format(header, "\n".join(no_application)))
def build_info(self, application):
embed = discord.Embed(colour=discord.Colour(0x417505))
embed.set_thumbnail(
url="https://minotar.net/helm/{0}/100.png".format(application.username))
embed.add_field(name="Application ID", value=application.id)
embed.add_field(name="Username", value=application.username.replace("_", "\\_"))
embed.add_field(name="Age", value=application.age)
embed.add_field(name="Type of Player", value=application.player_type)
embed.add_field(name="Ever been banned", value=application.ever_banned)
if application.ever_banned:
embed.add_field(name="Reason for being banned", value=application.ever_banned_explanation)
embed.add_field(name="Reference", value=application.reference)
embed.add_field(name="Read the Rules", value=application.read_rules)
embed.add_field(name="Date", value=application.date_display)
embed.add_field(name="Status", value=application.status)
return embed
@asyncio.coroutine
def on_error(self, event, *args, **kwargs):
print(sys.exc_info())
print("Exception raised by " + event)
error = '{0}\n{1}'.format(sys.exc_info()[1], ''.join(traceback.format_tb(sys.exc_info()[2])))
logger.error(error)
for user in self.error_users:
try:
user = discord.User(id=user)
yield from self.discord_message(user, '```python\n{}```'.format(error))
except:
pass
def run_bot(self):
self.run(self.token)

View File

@ -46,7 +46,7 @@ def clean(model, data):
attr = d
if '__' in d:
attr = d.split('__')[0]
if hasattr(model, attr):
if hasattr(model, attr) and attr != "api":
cleaned[d] = data[d]
return cleaned
@ -138,11 +138,11 @@ class WebAPI(View):
def access_level(self, user):
access = {'cpp': False, 'cpf': False, 'cpa': False}
if user.has_perm('auth.coreprotect_partial'):
if user.has_perm('minecraft_manager.coreprotect_partial'):
access['cpp'] = True
if user.has_perm('auth.coreprotect_full'):
if user.has_perm('minecraft_manager.coreprotect_full'):
access['cpf'] = True
if user.has_perm('auth.coreprotect_activity'):
if user.has_perm('minecraft_manager.coreprotect_activity'):
access['cpa'] = True
return access
@ -192,7 +192,7 @@ class PluginAPI(View):
json['message'] = "{0}'s application was submitted.".format(application.username)
json['extra'] = application.id
msg = mcm_utils.build_application(application)
mcm_api.discord_mcm(message='New Application!', embeds=msg)
mcm_api.discord_mcm(message='New Application!', embed=msg)
elif "application_action" == keyword:
if Application.objects.filter(id=post['application_id']).exists():
application = Application.objects.get(id=post['application_id'])
@ -279,11 +279,13 @@ class PluginAPI(View):
json['message'] = "Updated {0}".format(post['username'])
elif "register" == keyword:
player = Player.objects.get(uuid=post['uuid'])
password = mcm_api.generate_password()
if player.auth_user:
json['status'] = False
json['message'] = "You are already registered. To change your password, contact an Admin."
player.auth_user.password = password
player.auth_user.is_active = True
player.auth_user.save()
json['message'] = password
else:
password = mcm_api.generate_password()
user = User.objects.create_user(username=player.username.lower(), password=password)
user.save()
player.auth_user = user
@ -298,7 +300,7 @@ class PluginAPI(View):
link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/ticket', ticket.id))
msg = mcm_utils.build_ticket(ticket, link)
json['extra'] = {'id': ticket.id, 'link': link}
mcm_api.discord_mcm(embeds=msg, ping=True)
mcm_api.discord_mcm(embed=msg, ping=True)
except:
json['status'] = False
json['message'] = "Error while submitting ticket."
@ -311,7 +313,7 @@ class PluginAPI(View):
json['message'] = "Warning issued."
link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/note', warning.id))
msg = mcm_utils.build_warning(warning, link)
mcm_api.discord_mcm(embeds=msg)
mcm_api.discord_mcm(embed=msg)
except Exception as ex:
json['status'] = False
json['message'] = "Error while issuing warning."
@ -396,7 +398,31 @@ class ModelAPI(View):
return JsonResponse(json, safe=False)
def post(self, request, request_model):
pass
json = {"success": False, "message": ""}
if request_allowed(request, 'model_post_permission'):
post = request.POST
model = None
for m in apps.get_app_config('minecraft_manager').get_models():
if m._meta.model_name.upper() == request_model.upper():
model = m
break
if model:
keywords = clean(model, post)
if "id" in keywords:
try:
obj = model.objects.get(id=keywords["id"])
for key in keywords.keys():
setattr(obj, key, keywords[key])
obj.save()
json["success"] = True
json["message"] = "Model updated"
except Exception as ex:
print(ex)
json["message"] = "Could not update model"
else:
json["message"] = "Must provide an ID"
return JsonResponse(json)
class StatsAPI(View):

View File

@ -1,9 +0,0 @@
You will need to get this from your CP MySQL tables
NOTE: CoreProtect does not match up these IDs with the block's in-game ID, so air is not 0, stone is not 1, etc.
1. mysql -p <coreprotect database> -e "SELECT id, material FROM co_material_map" > blocks.txt
2. Open blocks.txt
3. Remove the header row
4. Remove spaces/tabs and delimit with a ','
e.g. 1,minecraft:air

View File

@ -1,55 +0,0 @@
-- Alerts
insert into minecraft_manager_alert (select * from whitelist_alert);
-- Applications
insert into minecraft_manager_application (select * from whitelist_application);
-- Players
insert into minecraft_manager_player (uuid, username, application_id, auth_user_id, last_seen, first_seen)
select wp.uuid, wp.username, (
select mma.id from minecraft_manager_application mma where mma.username = (
select wa.username from whitelist_application wa where wp.application_id = wa.id
)
), wp.auth_user_id, wp.last_seen, wp.first_seen from whitelist_player wp
;
-- Tickets
insert into minecraft_manager_ticket (message, priority, resolved, world, x, y, z, date, player_id, staff_id)
select wt.message, wt.priority, wt.resolved, wt.world, wt.x, wt.y, wt.z, wt.date, (
select mmp.id from minecraft_manager_player mmp where mmp.uuid = (
select wp.uuid from whitelist_player wp where wp.id = wt.player_id
)
), (
select au.id from auth_user au where au.username = (
select wp2.username from whitelist_player wp2 where wp2.id = wt.staff_id
)
) from whitelist_ticket wt
;
-- Warnings
insert into minecraft_manager_warning (message, severity, date, player_id, staff_id)
select ww.message, ww.severity, ww.date, (
select mmp.id from minecraft_manager_player mmp where mmp.uuid = (
select wp.uuid from whitelist_player wp where wp.id = ww.player_id
)
), (
select au.id from auth_user au where au.username = (
select wp2.username from whitelist_player wp2 where wp2.id = ww.staff_id
)
) from whitelist_warning ww
;
-- User Settings
insert into minecraft_manager_usersettings (default_results, default_theme, default_timezone, search_player_ip, show_timestamp_chat, last_ip, auth_user_id)
select default_results, default_theme, default_timezone, search_player_ip, show_timestamp_chat, last_ip, auth_user_id from whitelist_usersettings wu
;
-- Notes (This migration ONLY works if you are using standard whitelist app, aka only Tickets had notes)
-- The ignore is because there were some incorrectly encoded characters giving MySQL a hard time
insert ignore into minecraft_manager_note (ref_table, ref_id, message, last_update, author_id)
select wn.ref_table, (
select mmt.id from minecraft_manager_ticket mmt where mmt.message = (
select wt.message from whitelist_ticket wt where wt.id = wn.ref_id
)
), wn.message, wn.last_update, wn.author_id from whitelist_note wn where (select count(*) from whitelist_ticket wt2 where wt2.id = wn.ref_id) > 0
;

0
bot/__init__.py 100644
View File

View File

@ -1,18 +1,29 @@
import os, sys, django
import os
import sys
import django
# This block is assuming you will use this exact file
sep = os.sep
path = os.path.dirname(os.path.abspath(__file__))
path = path.split(sep)[:-3]
project = path[-1]
path = sep.join(path)
# What you need here is
# project = name of your main django project
# path = path to the root of your django project
# e.g. If your project is at /home/mcm/django1 and settings.py is at /home/mcm/django1/django2/settings.py
# project = django2
# path = /home/mcm/django1
sys.path.append(path)
print("Setting path for {0}: {1}".format(project, path))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{}.settings".format(project))
django.setup()
from django.conf import settings
from minecraft_manager.api.bot import Discord
from minecraft_manager.bot.discord import Discord
token = getattr(settings, 'DISCORD_BOT_TOKEN', None)
bot = Discord(token)
bot.run_bot()

240
bot/commands.py 100644
View File

@ -0,0 +1,240 @@
import discord
from discord.ext import commands
from django.contrib.auth.models import User
from django.db import close_old_connections
from minecraft_manager.api import api
from minecraft_manager.bot.utils import get_application
from minecraft_manager.utils import build_application
from minecraft_manager.models import Application, Player
class Commands(commands.Cog):
def __init__(self, bot):
self.bot = bot
async def cog_before_invoke(self, ctx):
# FIX STALE DB CONNECTIONS
close_old_connections()
async def cog_check(self, ctx):
# No DMs
if ctx.guild is None:
return False
# Check roles
if not hasattr(ctx.author, "roles"):
return False
for role in ctx.author.roles:
for auth_role in self.bot.auth_roles:
if role.id == auth_role:
return True
return False
def is_superuser(self, member: discord.Member):
for role in member.roles:
for auth_role in self.bot.superuser_roles:
if role.id == auth_role:
return True
return False
@commands.command()
async def help(self, ctx):
embed = discord.Embed(colour=discord.Colour(0x417505))
embed.set_thumbnail(
url="https://cdn.discordapp.com/avatars/454457830918062081/b5792489bc43d9e17b8f657880a17dd4.png")
embed.add_field(name="Minecraft Manager Help", value="-----------------------------")
embed.add_field(name="{}app search <username>".format(self.bot.prefix),
value="Search for applications by partial or exact username.")
embed.add_field(name="{}app info <app ID>".format(self.bot.prefix),
value="Get detailed information about a specific application.")
embed.add_field(name="{}app accept|deny <app ID>".format(self.bot.prefix), value="Take action on an application.")
embed.add_field(name="{}demote <username>".format(self.bot.prefix),
value="Demote a player to the role given to accepted applications.")
embed.add_field(name="{}compare".format(self.bot.prefix), value="Compare Discord users to the Whitelist.")
await self.bot.discord_message(ctx.message.channel, embed)
@commands.group("app", aliases=["application"])
async def app(self, ctx):
if ctx.invoked_subcommand is None:
await self.bot.discord_message(ctx.message.channel, "No sub-command supplied. Info, Search, Accept, or Deny.")
@commands.command()
async def info(self, ctx, *args):
await self._info(ctx, *args)
@app.command("info")
async def app_info(self, ctx, *args):
await self._info(ctx, *args)
async def _info(self, ctx, *args):
if len(args) == 0:
await self.bot.discord_message(ctx.message.channel, "Info requires an application ID or username.")
key = args[0]
is_id = True
try:
int(key)
except:
is_id = False
application = None
if is_id:
application = get_application(key)
if not application:
await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.")
return
else:
found = False
applications = Application.objects.filter(username__icontains=key)
if len(applications) == 0:
applications = Application.objects.filter(player__username__icontains=key)
if len(applications) == 1:
await self.bot.discord_message(ctx.message.channel, "**No applications matched, however there is a player match**")
application = applications[0]
found = True
elif len(applications) == 1:
application = applications[0]
found = True
if not found:
await self.bot.discord_message(ctx.message.channel, "An exact Application could not be found. Try search instead.")
return
await self.bot.discord_message(ctx.message.channel, build_application(application))
@commands.command()
async def accept(self, ctx, app_id: int):
await self._accept(ctx, app_id)
@app.command("accept")
async def app_accept(self, ctx, app_id: int):
await self._accept(ctx, app_id)
async def _accept(self, ctx, app_id: int):
application = get_application(app_id)
if not application:
await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.")
return
if not application.accepted:
application.accepted = True
application.save()
if Player.objects.filter(username__iexact=application.username).exists():
player = Player.objects.get(username__iexact=application.username)
player.application_id = application.id
player.save()
await self.bot.discord_message(ctx.message.channel, "App ID **{0}** was successfully accepted.".format(app_id))
if not api.plugin(api.PLUGIN_ACCEPT, application.username):
await self.bot.discord_message(ctx.message.channel, "Could not accept in-game, is the server running?")
@commands.command()
async def deny(self, ctx, app_id: int):
await self._deny(ctx, app_id)
@app.command("deny")
async def app_deny(self, ctx, app_id: int):
await self._deny(ctx, app_id)
async def _deny(self, ctx, app_id: int):
application = get_application(app_id)
if not application:
await self.bot.discord_message(ctx.message.channel, "An Application with that ID doesn't exist.")
return
if not application.accepted:
application.accepted = False
application.save()
if Player.objects.filter(username__iexact=application.username).exists():
player = Player.objects.get(username__iexact=application.username)
player.application_id = application.id
player.save()
await self.bot.discord_message(ctx.message.channel, "App ID **{0}** was successfully denied.".format(app_id))
if not api.plugin(api.PLUGIN_DENY, application.username):
await self.bot.discord_message(ctx.message.channel, "Could not deny in-game, is the server running?")
@commands.command()
async def search(self, ctx, search: str):
await self._search(ctx, search)
@app.command("search")
async def app_search(self, ctx, search: str):
await self._search(ctx, search)
async def _search(self, ctx, search: str):
applications = Application.objects.filter(username__icontains=search)[:10]
count = Application.objects.filter(username__icontains=search).count()
if count > 0:
if count == 1:
info = build_application(applications[0])
else:
info = "**Found the following applications**"
for app in applications:
info += "\n{0} - {1} ({2})".format(app.id, app.username.replace("_", "\\_"), app.status)
if count > 10:
info += "\n**This is only 10 applications out of {0} found. Please narrow your search if possible.**".format(len(applications))
else:
players = Player.objects.filter(username__icontains=search, application__isnull=False)[:10]
count = Player.objects.filter(username__icontains=search, application__isnull=False).count()
if count > 0:
if count == 1:
await self.bot.discord_message(ctx.message.channel, "**No applications matched, however there is a player match**")
info = build_application(players[0].application)
else:
info = "**No applications matched, however there are player matches**"
for player in players:
app = player.application
info += "\n{0} - {1} AKA {2} ({3})".format(app.id, app.username.replace("_", "\\_"),
player.username.replace("_", "\\_"), app.status)
if count > 10:
info += "\n**This is only 10 players out of {0} found. Please narrow your search if possible.**".format(len(players))
else:
info = "No applications matched that search."
await self.bot.discord_message(ctx.message.channel, info)
@commands.command()
async def demote(self, ctx, username: str):
if not self.is_superuser(ctx.author):
return
await ctx.message.delete()
if api.plugin(api.PLUGIN_DEMOTE, username):
await self.bot.discord_message(ctx.message.channel, "{} has been demoted in-game.".format(username))
else:
await self.bot.discord_message(ctx.message.channel, "{} could not be demoted in-game, is the server running?".format(username))
if User.objects.filter(username__iexact=username).exists():
user = User.objects.get(username__iexact=username)
user.is_active = False
user.save()
await self.bot.discord_message(ctx.message.channel, "{} has been de-activated in MCM.".format(username))
else:
await self.bot.discord_message(ctx.message.channel, "{} could not be found in MCM, is their account up-to-date?".format(username))
@commands.command()
async def compare(self, ctx):
await ctx.message.delete()
await ctx.message.channel.trigger_typing()
no_player = []
no_application = []
for member in ctx.message.guild.members:
if member.bot:
continue
name = member.nick if member.nick else member.name
try:
Player.objects.get(username__iexact=name)
except:
no_player.append(name)
try:
Application.objects.get(username__iexact=name)
except:
no_player = no_player[:-1]
no_application.append(name)
if no_player:
header = "**The following users have an application match, but no player match on the whitelist:**\n"
await self.bot.discord_message(ctx.author, "{}```{}```".format(header, "\n".join(no_player)))
if no_application:
header = "**The following users do not have an application or player match on the whitelist:**\n"
await self.bot.discord_message(ctx.author, "{}```{}```".format(header, "\n".join(no_application)))
def setup(bot):
bot.add_cog(Commands(bot))

95
bot/discord.py 100644
View File

@ -0,0 +1,95 @@
import asyncio
import logging
import traceback
import discord
from discord.ext import commands
from django.conf import settings
from minecraft_manager.models import Application, Ticket
from minecraft_manager.utils import url_path
logger = logging.getLogger(__name__)
description = '''
A Discord bot connected to an MCM instance.
'''
class Discord(commands.Bot):
discord_game = 'MCM'
prefix = getattr(settings, 'DISCORD_BOT_PREFIX', '!')
auth_roles = getattr(settings, 'DISCORD_BOT_ROLES', [])
superuser_roles = getattr(settings, 'DISCORD_SUPERUSER_ROLES', [])
error_users = getattr(settings, 'DISCORD_ERROR_USERS', [])
def __init__(self, token):
super().__init__(command_prefix=self.prefix, description=description, case_insensitive=True, help_command=None,
activity=discord.Game(name=self.discord_game))
self.token = token
self.load_extension("minecraft_manager.bot.commands")
async def on_ready(self):
print('Logged in as')
print(self.user.name)
print(self.user.id)
print(discord.__version__)
print('Voice Loaded: {0}'.format(discord.opus.is_loaded()))
print('OAuth URL: https://discordapp.com/api/oauth2/authorize?client_id={0}&permissions=0&scope=bot'.format(self.user.id))
print('------')
print('Logged in as {0} ({1}) with discord.py v{2}'.format(self.user.name, self.user.id, discord.__version__))
channel_id = getattr(settings, 'DISCORD_MCM_CHANNEL', None)
if channel_id:
channel = self.get_channel(channel_id)
embed = discord.Embed(color=8311585)
content = ""
unanswered_applications = Application.objects.filter(accepted=None)
if len(unanswered_applications) > 0:
link = url_path(settings.MCM_BASE_LINK, 'dashboard/application')
content += "[Unanswered Applications: {}]({})".format(len(unanswered_applications), link)
unclaimed_tickets = Ticket.objects.filter(staff=None, resolved=False)
if len(unclaimed_tickets) > 0:
link = url_path(settings.MCM_BASE_LINK, 'dashboard/ticket')
if content:
content += "\n\n"
content += "[Unclaimed Tickets: {}]({})".format(len(unclaimed_tickets), link)
if content:
embed.title = "MCM Reminder"
embed.description = content
await self.discord_message(channel, embed)
async def discord_message(self, dest, message):
if isinstance(message, discord.Embed):
for idx, field in enumerate(message.fields):
if not field.value:
message.set_field_at(idx, name=field.name, value="N/A")
await dest.send(embed=message)
else:
await dest.send(message)
async def on_command_error(self, context, exception):
if not isinstance(exception, commands.CommandInvokeError):
return
if hasattr(exception, "original"):
error = ''.join(traceback.format_tb(exception.original.__traceback__))
else:
error = exception
logger.error(error)
for user_id in self.error_users:
user = self.get_user(user_id)
if user:
await self.discord_message(user, '```python\n{}```'.format(error))
def run_bot(self):
loop = asyncio.get_event_loop()
try:
loop.run_until_complete(self.start(self.token))
except KeyboardInterrupt:
logger.info("Bot received keyboard interrupt")
except Exception as e:
print(e)
logger.info('Bot encountered the following unhandled exception %s', e)
finally:
loop.run_until_complete(self.logout())
logger.info("Bot shutting down...")

8
bot/utils.py 100644
View File

@ -0,0 +1,8 @@
from minecraft_manager.models import Application
def get_application(app_id):
try:
return Application.objects.get(id=app_id)
except:
return None

View File

@ -25,11 +25,27 @@ Optional
``DISCORD_NOTIFICATION_WEBHOOK`` - The URL for the webhook used for notifications.
``DISCORD_PING_LIST`` - A list of Discord Role IDs to ping whenever certain messages are sent.
``DISCORD_MCM_WEBHOOK`` - The URL for the webhook used for Applications, Tickets, and Warnings.
``DISCORD_INVITE`` - The invite code to your Discord, for after a player applies on the web form.
``DISCORD_BOT_TOKEN`` - The token to use to run the Discord bot. This must be generated by you in the Discord developer area.
``DISCORD_PING_LIST`` - A list of Discord Role IDs to ping whenever certain messages are sent.
``DISCORD_BOT_PREFIX`` - The prefix to use for Discord bot commands. Set to ``!`` by default.
``DISCORD_BOT_ROLES`` - A list of Discord Roles allowed to use the bot. If this list is empty, no one can use the bot!
``DISCORD_SUPERUSER_ROLES`` - A list of Discord Roles allowed to use the superuser commands.
``DISCORD_ERROR_USERS`` - A list of user IDs to send errors to.
``DISCORD_MCM_CHANNEL`` - The ID for the channel used for Applications, Tickets, and Warnings.
``DISCORD_NOTIFICATION_CHANNEL`` - The ID for the channel used for notifications.
``INVITE_LINK`` - The invite link to your community.
``INVITE_LABEL`` - The invite label for your community.
``DYNMAP_URL`` - The URL to your dynmap if you have one. Leave blank if you'd rather use a static background for web forms.
@ -51,14 +67,6 @@ Optional
``COREPROTECT_ACTIVITY_URL`` - The URL to your CoreProtect Activity Web UI, if it exists.
``DISCORD_BOT_TOKEN`` - The token to use to run the Discord bot. This must be generated by you in the Discord developer area.
``DISCORD_BOT_PREFIX`` - The prefix to use for Discord bot commands. Set to ``!`` by default.
``DISCORD_BOT_ROLES`` - A list of Discord Roles allowed to use the bot. If this list is empty, no one can use the bot!
``DISCORD_BOT_NEW_MEMBER_ROLES`` - A list of Discord Roles to give new players when they register.
``CAPTCHA_SECRET`` - Your secret key used for reCAPTCHA
``STATS_FILTER`` - A python list of partial strings used to filter out stats. e.g. ``['broken', 'dropped', 'picked_up']`` to filter out broken, dropped and picked up stats

43
external/views.py vendored
View File

@ -1,5 +1,5 @@
from django.views.generic import View
from django.shortcuts import render, reverse, redirect
from django.shortcuts import render
from django.conf import settings
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
@ -8,12 +8,13 @@ import minecraft_manager.api.api as mcm_api
import minecraft_manager.utils as mcm_utils
import minecraft_manager.external.stats as mcm_stats
from minecraft_manager.models import Player
import random, yaml, os, json, datetime, pytz
import random, yaml, os
def config():
data = {}
data['discord_invite'] = getattr(settings, "DISCORD_INVITE", "#")
data['invite_link'] = getattr(settings, "INVITE_LINK", "#")
data['invite_label'] = getattr(settings, "INVITE_LABEL", "community")
dynmap_url = getattr(settings, "DYNMAP_URL", "")
data['dynmap_url'] = dynmap_url
@ -45,12 +46,16 @@ def config():
def rules():
path = os.path.join(settings.MINECRAFT_BASE_DIR, "plugins/MinecraftManager/config.yml")
with open(path) as config_file:
config = yaml.load(config_file)
data = config['rules']['rules']
if config['rules']['application']['validate']:
data.append("The answer to the final question is \"{}\"".format(config['rules']['application']['answer']))
cfg = yaml.safe_load(config_file)
data = cfg['rules']['rules']
if cfg['rules']['application']['validate']:
data.append("The answer to the final question is \"{}\"".format(cfg['rules']['application']['answer']))
return data
return {
"rules": data,
"validate": cfg['rules']['application']['validate'],
"answer": cfg['rules']['application']['answer']
}
@method_decorator(csrf_exempt, name='dispatch')
@ -59,27 +64,31 @@ class Apply(View):
def get(self, request):
form = ApplicationForm()
return render(request, 'minecraft_manager/external/apply.html',
{'form': form.as_p(), 'rules': rules(), 'valid': False, 'map': config(),
'captcha': hasattr(settings, "CAPTCHA_SECRET")})
{'form': form.as_p(), 'rules': rules()["rules"], 'valid': False, 'map': config(),
'captcha': getattr(settings, "CAPTCHA_SITE", "")})
def post(self, request):
form = ApplicationForm(request.POST)
valid_username = mcm_utils.validate_username(form.data['username'])
captcha = mcm_utils.Captcha(request.POST)
valid = form.is_valid()
if valid and valid_username and captcha.success:
r = rules()
valid_answer = not r["validate"] or r["answer"] == form.data['read_rules']
if valid and valid_username and valid_answer and captcha.success:
app = form.save()
msg = mcm_utils.build_application(app)
mcm_api.discord_mcm(message='New Application!', embeds=msg)
mcm_api.discord_mcm(message='New Application!', embed=msg)
mcm_api.plugin("application", "{0} {1}".format(form.data['username'], app.id))
else:
for error in captcha.errors:
form.add_error(None, error)
if not valid_username:
form.add_error(None, "That username is not a premium Minecraft account")
if not valid_answer:
form.add_error(None, "Please read the rules again")
return render(request, 'minecraft_manager/external/apply.html',
{'form': form.as_p(), 'rules': rules(), 'valid': valid and valid_username and captcha.success, 'map': config(),
'captcha': hasattr(settings, "CAPTCHA_SECRET")})
{'form': form.as_p(), 'rules': r["rules"], 'valid': valid and valid_username and valid_answer and captcha.success, 'map': config(),
'captcha': getattr(settings, "CAPTCHA_SITE", "")})
@method_decorator(csrf_exempt, name='dispatch')
@ -89,7 +98,7 @@ class Ticket(View):
form = TicketForm()
return render(request, 'minecraft_manager/external/ticket.html',
{'form': form.as_p(), 'valid': False, 'map': config(),
'captcha': hasattr(settings, "CAPTCHA_SECRET")})
'captcha': getattr(settings, "CAPTCHA_SITE", "")})
def post(self, request):
post = request.POST.copy()
@ -109,7 +118,7 @@ class Ticket(View):
# Create the message to send to Discord
link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/ticket', ticket.id))
msg = mcm_utils.build_ticket(ticket, link)
mcm_api.discord_mcm(message="New Ticket", embeds=msg, ping=True)
mcm_api.discord_mcm(message="New Ticket", embed=msg, ping=True)
mcm_api.plugin("ticket", "{0} {1} {2}".format(username, ticket.id, link))
else:
for error in captcha.errors:
@ -120,7 +129,7 @@ class Ticket(View):
form.data['player'] = username
return render(request, 'minecraft_manager/external/ticket.html',
{'form': form.as_p(), 'valid': valid and captcha.success, 'map': config(),
'captcha': hasattr(settings, "CAPTCHA_SECRET")})
'captcha': getattr(settings, "CAPTCHA_SITE", "")})
@method_decorator(csrf_exempt, name='dispatch')

File diff suppressed because one or more lines are too long

View File

@ -13,8 +13,8 @@ class MinecraftManagerUser(User):
class Meta:
proxy = True
permissions = (
('minecraft_manager_bots', 'Can use the bot control page'),
('minecraft_manager_chat', 'Can use chat page'),
('bots', 'Can use the bot control page'),
('chat', 'Can use chat page'),
)

View File

@ -46,11 +46,11 @@ def overview_data():
# Percentage
data['percentage'] = {
'accepted': round((data['total']['application']['accepted'] /
data['total']['application']['all'] if data['total']['application']['all'] != 0 else 1) * 100, 2),
data['total']['application']['all'] if data['total']['application']['all'] != 0 else 1) * 100, 2),
'banned': round((data['total']['player']['banned'] /
data['total']['player']['all'] if data['total']['player']['all'] != 0 else 1) * 100, 2),
data['total']['player']['all'] if data['total']['player']['all'] != 0 else 1) * 100, 2),
'applied': round((data['total']['application']['all'] /
data['total']['player']['all'] if data['total']['player']['all'] != 0 else 1) * 100, 2)
data['total']['player']['all'] if data['total']['player']['all'] != 0 else 1) * 100, 2)
}
# Unique logins

View File

@ -67,4 +67,9 @@
.rule {
margin-bottom: .5em;
}
.errorlist {
color: #D8000C;
background-color: #FFD2D2;
}

View File

@ -1,16 +1,14 @@
{% extends "minecraft_manager/dashboard.html" %}
{% load csrf_html %}
{% load getattribute %}
{% block title %}Bots{% endblock %}
{% block section %}
<div id="content">
<!-- {{ form.status }} -->
{% for bot in form.bots %}
<p>{{ bot.name }}: <span class="label {% if form|getattribute:bot.name is True %}label-success{% else %}label-danger{% endif %}">{% if form|getattribute:bot.name is True %}Started{% else %}Stopped{% endif %}</span></p>
{% for bot in bots %}
<p>{{ bot.name }}: <span class="label {% if bot.status is True %}label-success{% else %}label-danger{% endif %}">{{ bot.display }}</span></p>
<form action="" method="post">{% autoescape off %}{% get_csrf_html request %}{% endautoescape %}
<button class="btn btn-primary{% if form|getattribute:bot.name is True %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="start" {% if form|getattribute:bot.name is True %} disabled="disabled"{% endif %}>Start</button>
<button class="btn btn-primary{% if form|getattribute:bot.name is False %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="stop" {% if form|getattribute:bot.name is False %}disabled="disabled"{% endif %}>Stop</button>
<button class="btn btn-primary{% if form|getattribute:bot.name is False %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="restart" {% if form|getattribute:bot.name is False %}disabled="disabled"{% endif %}>Restart</button>
<button class="btn btn-primary{% if bot.status is True %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="start" {% if bot.status is True %} disabled="disabled"{% endif %}>Start</button>
<button class="btn btn-primary{% if bot.status is False %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="stop" {% if bot.status is False %}disabled="disabled"{% endif %}>Stop</button>
<button class="btn btn-primary{% if bot.status is False %} disabled{% endif %}" type="submit" name="{{ bot.name }}" value="restart" {% if bot.status is False %}disabled="disabled"{% endif %}>Restart</button>
</form>
<br/>
{% endfor %}

View File

@ -1,12 +1,13 @@
{% extends "minecraft_manager/dashboard.html" %}
{% load template_settings %}
{% block title %}CoreProtect GUI{% endblock %}
{% block section %}
<a href="{% template_settings 'COREPROTECT_WEB_URL' %}?username={{ user.username }}" target="_blank">Fullscreen</a>
<iframe id="cpgui" width="100%" height="750px" src="{% template_settings 'COREPROTECT_WEB_URL' %}?username={{ user.username }}">Loading...</iframe>
<script>
var height = $(window).height();
//$("#cpgui").height(height * .8);
</script>
{% endblock section %}
{% if show_gui and show_activity %}
<li class="dropdown">
<a class="dropdown-toggle " href="#" role="button" data-toggle="dropdown" id="cpDropdown">CoreProtect</a>
<ul class="dropdown-menu" aria-labelledby="cpDropdown">
<li><a target="_blank" href="{{ url_gui }}">Web GUI</a></li>
<li><a target="_blank" href="{{ url_activity }}">Activity Monitor</a></li>
</ul>
</li>
{% elif show_gui %}
<li><a target="_blank" class="dropdown-item btn" href="{{ url_gui }}">CoreProtect GUI</a></li>
{% elif show_activity %}
<li><a target="_blank" class="dropdown-item btn" href="{{ url_activity }}">Activity Monitor</a></li>
{% endif %}

View File

@ -3,6 +3,7 @@
{% load csrf_html %}
{% load sidebar %}
{% load template_settings %}
{% load coreprotect %}
{% block bootstrap %}
{% if user.usersettings.default_theme == 'DA' %}
<link rel="stylesheet" href="{% static "minecraft_manager/css/bootswatch-darkly.css" %}">
@ -23,21 +24,7 @@
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
{% template_settings 'COREPROTECT_WEB_URL' as CPW %}
{% template_settings 'COREPROTECT_ACTIVITY_URL' as CPA %}
{% if CPW %}
{% if perms.auth.coreprotect_activity and CPA %}
<li class="dropdown">
<a class="dropdown-toggle " href="#" role="button" data-toggle="dropdown" id="cpDropdown">CoreProtect</a>
<ul class="dropdown-menu" aria-labelledby="cpDropdown">
<li><a href="{% url 'coreprotect' %}">Web GUI</a></li>
<li><a href="{% url 'activity' %}">Activity Monitor</a></li>
</ul>
</li>
{% elif perms.auth.coreprotect_partial or perms.auth.coreprotect_full %}
<li><a class="dropdown-item btn" href="{% url 'coreprotect' %}">CoreProtect</a></li>
{% endif %}
{% endif %}
{% coreprotect %}
<li class="dropdown">
<a class="dropdown-toggle " href="#" role="button" data-toggle="dropdown" id="accountDropdown">{{ user.username }}</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="accountDropdown">

View File

@ -17,7 +17,7 @@
<br/>
We will get back to you soon.
<br/>
Consider joining our <a href="https://discord.gg/{{ map.discord_invite }}">Discord</a></h2>
Consider joining our <a href="{{ map.invite_link }}">{{ map.invite_label }}</a></h2>
{% endblock %}
{% endif %}

View File

@ -21,7 +21,7 @@
{% else %}
<form method="{% block method %}POST{% endblock %}">
{% block form %}{{ form }}{% endblock %}<br/><br/>
{% if captcha %}<div class="g-recaptcha" data-sitekey="6LeLcGEUAAAAAMMpHR-7VL6Q3nNV5v03S5sq1Ddz"></div>{% endif %}
{% if captcha %}<div class="g-recaptcha" data-sitekey="{{ captcha }}"></div>{% endif %}
<button type="submit">{% block submit %}Submit{% endblock %}</button>
</form>
{% endif %}

View File

@ -3,22 +3,37 @@
{% block title %}Overview{% endblock %}
{% block head %}
<script src="{% static 'minecraft_manager/js/chart.min.js' %}"></script>
<script>
$(document).ready(function() {
$(".staff-inactive").hide();
$("#active-only").change(function() {
if ($(this)[0].checked) {
$(".staff-inactive").hide();
} else {
$(".staff-inactive").show();
}
});
});
</script>
{% endblock %}
{% block section %}
<div id="content">
{% if request.user.is_staff %}
<div class="panel panel-danger">
<div class="panel-body">
<h4><span class="label label-danger">Admin Area</span></h4>
<h3>Resolved Tickets</h3>
<div class="row">
{% for staff in data.resolved %}
<div class="col-xs-6 col-md-4">
<p><span class="label label-{% if staff.active %}success{% else %}danger{% endif %}">{% if staff.active %}Active{% else %}Inactive{% endif %}</span> {{ staff.username }}: {{ staff.tickets }}</p>
<div class="panel-body">
<h4><span class="label label-danger">Admin Area</span></h4>
<h3>Resolved Tickets</h3>
<label>Active Only
<input id="active-only" type="checkbox" checked/>
</label>
<div class="row">
{% for staff in data.resolved %}
<div class="col-xs-6 col-md-4{% if not staff.active %} staff-inactive{% endif %}">
<p><span class="label label-{% if staff.active %}success{% else %}danger{% endif %}">{% if staff.active %}Active{% else %}Inactive{% endif %}</span> {{ staff.username }}: {{ staff.tickets }}</p>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<hr/>
{% endif %}

View File

@ -0,0 +1,20 @@
from django.template import Library
from django.shortcuts import reverse
register = Library()
@register.inclusion_tag('minecraft_manager/coreprotect.html', takes_context=True)
def coreprotect(context):
user = context.get("user", None)
data = {"show_gui": False, "show_activity": False, "url_gui": "", "url_activity": ""}
if user:
try:
data["show_gui"] = user.has_perm("django_coreprotect.gui")
data["url_gui"] = reverse("coreprotect_gui")
data["show_activity"] = user.has_perm("django_coreprotect.activity")
data["url_activity"] = reverse("coreprotect_activity")
except:
pass
return data

View File

@ -31,8 +31,7 @@ def get_sidebar(current_app, request):
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-wrench"></span>&nbsp;&nbsp;Report</a></li>'.format('class="active"' if current_app == 'report' else '', reverse('report'))
show_chat = True if getattr(settings, 'GLOBAL_LOG', None) is not None else False
if show_chat and request.user.has_perm('auth.chat'):
if show_chat and request.user.has_perm('minecraft_manager.chat'):
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-comment"></span>&nbsp;&nbsp;Chat</a></li>'.format('class="active"' if current_app == 'chat' else '', reverse('chat'))
if request.user.has_perm('auth.bots'):
ret += '<li {}><a href="{}"><span class="glyphicon glyphicon-flash"></span>&nbsp;&nbsp;Bots</a></li>'.format('class="active"' if current_app == 'bots' else '', reverse('bots'))
return ret

71
urls.py
View File

@ -1,74 +1,43 @@
from django.conf.urls import url
from django.views.generic import RedirectView
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import login_required, permission_required
import minecraft_manager.views as mcm
urlpatterns = [
url(r'^$', RedirectView.as_view(pattern_name='overview')),
#Dashboard
# Dashboard
url(r'^dashboard/overview/$', login_required(mcm.Overview.as_view()), name="overview"),
url(r'^dashboard/ban/$', login_required(mcm.Ban.as_view()), name="ban"),
#CoreProtect
url(r'^dashboard/coreprotect/$', login_required(mcm.CoreProtect.as_view()), name="coreprotect"),
url(r'^dashboard/activity/$', login_required(mcm.Activity.as_view()), name="activity"),
#Alerts
# Alerts
url(r'^dashboard/alert/$', login_required(mcm.Alert.as_view()), name="alert"),
url(r'^dashboard/alert/(?P<alert_id>[0-9]{1,5})/$', login_required(mcm.AlertInfo.as_view())),
#Applications
# Applications
url(r'^dashboard/application/$', login_required(mcm.Application.as_view()), name="application"),
url(r'^dashboard/reference/$', login_required(mcm.Reference.as_view()), name="reference"),
url(r'^dashboard/application/(?P<application_id>[0-9]{1,5})/$', login_required(mcm.ApplicationInfo.as_view())),
#Players
# Players
url(r'^dashboard/player/$', login_required(mcm.Player.as_view()), name="player"),
url(r'^dashboard/player/(?P<player_id>[0-9]{1,5})/$', login_required(mcm.PlayerInfo.as_view())),
#Tickets
# Tickets
url(r'^dashboard/ticket/$', login_required(mcm.Ticket.as_view()), name="ticket"),
url(r'^dashboard/ticket/(?P<ticket_id>[0-9]{1,5})/$', login_required(mcm.TicketInfo.as_view())),
#Warnings
# Warnings
url(r'^dashboard/note/$', login_required(mcm.Note.as_view()), name="note"),
url(r'^dashboard/note/(?P<note_id>[0-9]{1,5})/$', login_required(mcm.NoteInfo.as_view())),
url(r'^dashboard/note/add$', login_required(mcm.NoteAdd.as_view()), name="note_add"),
#IP
# IP
url(r'^dashboard/ip/(?P<ip_id>[0-9]{1,5})/$', login_required(mcm.IP.as_view()), name="ip"),
#Report
# Report
url(r'^report/$', login_required(mcm.Report.as_view()), name="report"),
#Chat
url(r'^dashboard/chat/$', login_required(mcm.Chat.as_view()), name="chat"),
#Bots
url(r'^dashboard/bots/$', login_required(mcm.Bots.as_view()), name="bots"),
# Chat
url(r'^dashboard/chat/$', permission_required('minecraft_manager.chat')(mcm.Chat.as_view()), name="chat"),
]
# Possible future feature
# from django.conf import settings
#
# LOGIN_REQUIRED = settings.LOGIN_REQUIRED if hasattr(settings, 'LOGIN_REQUIRED') else False
#
# urlpatterns = [
# #Dashboard
# url(r'^dashboard/overview/$', login_required(mcm.Overview.as_view()) if LOGIN_REQUIRED else mcm.Overview.as_view(), name="overview"),
# url(r'^dashboard/coreprotect/$', login_required(mcm.CoreProtect.as_view()) if LOGIN_REQUIRED else mcm.CoreProtect.as_view(), name="coreprotect"),
# url(r'^dashboard/activity/$', login_required(mcm.Activity.as_view()) if LOGIN_REQUIRED else mcm.Activity.as_view(), name="activity"),
# url(r'^dashboard/ban/$', login_required(mcm.Ban.as_view()) if LOGIN_REQUIRED else mcm.Ban.as_view(), name="ban"),
# #Alerts
# url(r'^dashboard/alert/$', login_required(mcm.Alert.as_view()) if LOGIN_REQUIRED else mcm.Alert.as_view(), name="alert"),
# url(r'^dashboard/alert/(?P<alert_id>[0-9]{1,5})/$', login_required(mcm.AlertInfo.as_view()) if LOGIN_REQUIRED else mcm.AlertInfo.as_view()),
# #Applications
# url(r'^dashboard/application/$', login_required(mcm.Application.as_view()) if LOGIN_REQUIRED else mcm.Application.as_view(), name="application"),
# url(r'^dashboard/application/(?P<application_id>[0-9]{1,5})/$', login_required(mcm.ApplicationInfo.as_view()) if LOGIN_REQUIRED else mcm.ApplicationInfo.as_view()),
# #Players
# url(r'^dashboard/player/$', login_required(mcm.Player.as_view()) if LOGIN_REQUIRED else mcm.Player.as_view(), name="player"),
# url(r'^dashboard/player/(?P<player_id>[0-9]{1,5})/$', login_required(mcm.PlayerInfo.as_view()) if LOGIN_REQUIRED else mcm.PlayerInfo.as_view()),
# #Tickets
# url(r'^dashboard/ticket/$', login_required(mcm.Ticket.as_view()) if LOGIN_REQUIRED else mcm.Ticket.as_view(), name="ticket"),
# url(r'^dashboard/ticket/(?P<ticket_id>[0-9]{1,5})/$', login_required(mcm.TicketInfo.as_view()) if LOGIN_REQUIRED else mcm.TicketInfo.as_view()),
# #Warnings
# url(r'^dashboard/warning/$', login_required(mcm.Warning.as_view()) if LOGIN_REQUIRED else mcm.Warning.as_view(), name="warning"),
# url(r'^dashboard/warning/(?P<warning_id>[0-9]{1,5})/$', login_required(mcm.WarningInfo.as_view()) if LOGIN_REQUIRED else mcm.WarningInfo.as_view()),
# url(r'^dashboard/warning/add$', login_required(mcm.WarningAdd.as_view()) if LOGIN_REQUIRED else mcm.WarningAdd.as_view(), name="warning_add"),
# #Chat
# url(r'^dashboard/chat/$', login_required(mcm.Chat.as_view()) if LOGIN_REQUIRED else mcm.Chat.as_view(), name="chat"),
# #Bots
# url(r'^dashboard/bots/$', login_required(mcm.Bots.as_view()) if LOGIN_REQUIRED else mcm.Bots.as_view(), name="bots"),
# ]

View File

@ -38,7 +38,8 @@ def build_application(application):
embed.add_field(name="Read the Rules", value=application.read_rules)
embed.add_field(name="Date", value=application.date_display)
embed.add_field(name="Status", value=application.status)
return [embed.to_dict()]
embed.add_field(name="Link", value="{}".format(url_path(settings.MCM_BASE_LINK, 'dashboard/application', application.id)))
return embed
def build_ticket(ticket, link):
@ -53,7 +54,7 @@ def build_ticket(ticket, link):
embed.add_field(name="Location", value=ticket.location)
embed.add_field(name="Message", value=ticket.message)
embed.add_field(name="Link", value=link)
return [embed.to_dict()]
return embed
def build_warning(warning, link):
@ -66,7 +67,7 @@ def build_warning(warning, link):
embed.add_field(name="Importance", value=warning.importance_display)
embed.add_field(name="Message", value=warning.message)
embed.add_field(name="Link", value=link)
return [embed.to_dict()]
return embed
def validate_username(username):

View File

@ -18,8 +18,6 @@ from minecraft_manager.overview import overview_data
from minecraft_manager.utils import resolve_player
import minecraft_manager.api.api as API
import subprocess
class Overview(View):
@ -448,59 +446,3 @@ class Chat(View):
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()})