2018-09-20 02:56:17 +00:00
import discord , logging , re , sys , traceback , asyncio , datetime , os , time
from minecraft_manager . models import Application , Player
from minecraft_manager . api import api
2018-10-21 03:59:10 +00:00
from django . contrib . auth . models import User
2018-09-20 02:56:17 +00:00
from django . conf import settings
from django . db import close_old_connections
from threading import Thread
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 ' , [ ] )
2018-12-10 20:58:53 +00:00
new_member_roles = getattr ( settings , ' DISCORD_BOT_NEW_MEMBER_ROLES ' , [ ] )
2018-09-20 02:56:17 +00:00
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 ]
2018-12-09 16:22:52 +00:00
# FIX STALE DB CONNECTIONS
close_old_connections ( )
# IF NOT A MEMBER YET
if len ( member_roles ) == 1 :
# REGISTER
match = re . match ( " [ {0} ]register ( \ S+)?$ " . format ( self . prefix ) , message . content )
if match :
search = match . group ( 1 )
2018-12-10 21:10:41 +00:00
count = Application . objects . filter ( username__iexact = search , accepted = True ) . count ( )
2018-12-09 16:22:52 +00:00
2018-12-10 18:44:31 +00:00
if count == 0 :
2018-12-10 21:10:41 +00:00
count = Player . objects . filter ( username__iexact = search , application__accepted = True ) . count ( )
2018-12-10 18:44:31 +00:00
2018-12-09 16:22:52 +00:00
if count > 0 :
2018-12-10 22:20:23 +00:00
count = Player . objects . filter ( username__iexact = search , application__accepted = True ) . count ( )
if count == 0 :
msg = " {0} , please join the server once before joining the discord. " . format ( message . author . mention )
yield from self . discord_message ( message . channel , msg )
return
2018-12-10 18:44:31 +00:00
2018-12-10 22:20:23 +00:00
player = Player . objects . filter ( username__iexact = search , application__accepted = True ) . all ( ) [ 0 ]
nickname = player . username
2018-12-10 19:26:14 +00:00
2018-12-10 22:20:23 +00:00
if not player . is_banned :
on_server = False
for member in message . server . members :
if member is message . author :
continue
2018-12-10 20:58:53 +00:00
2018-12-10 22:20:23 +00:00
if member . display_name == nickname :
on_server = True
2018-12-10 19:26:14 +00:00
2018-12-10 22:20:23 +00:00
if on_server :
msg = " {0} , a member with that name is already exists, please contact the staff " . format ( message . author . mention )
yield from self . discord_message ( message . channel , msg )
2018-12-10 18:44:31 +00:00
else :
2018-12-10 22:20:23 +00:00
for role_id in self . new_member_roles :
role = discord . utils . get ( message . server . roles , id = role_id )
yield from self . add_roles ( message . author , role )
msg = " Successfully added {0} as a member " . format ( nickname )
yield from self . change_nickname ( message . author , nickname )
2018-12-10 18:44:31 +00:00
yield from self . discord_message ( message . channel , msg )
2018-12-10 22:20:23 +00:00
else :
msg = " {0} You are currently banned. " . format ( message . author . mention )
yield from self . discord_message ( message . channel , msg )
2018-12-09 16:22:52 +00:00
return
else :
2018-12-11 16:53:41 +00:00
app = Application . objects . filter ( username__iexact = search ) . first ( )
2018-12-11 16:39:28 +00:00
2018-12-11 16:53:41 +00:00
if app is None :
2018-12-11 16:39:28 +00:00
msg = " {0} , an application for {1} could not be found, please check your username and make sure you have applied. " . format ( message . author . mention , search )
2018-12-11 16:53:41 +00:00
elif app . accepted is None :
msg = " {0} , your application is still in review, please try again when you have been accepted on the server. " . format ( message . author . mention )
elif not app . accepted :
2018-12-11 16:39:28 +00:00
msg = " {0} , your application has been denied. Best of luck finding a new server! " . format ( message . author . mention )
2018-12-11 16:53:41 +00:00
else :
msg = " {0} , an error has occurred. Please try again in a few minutes. " . format ( message . author . mention )
2018-12-11 16:39:28 +00:00
2018-12-09 16:22:52 +00:00
yield from self . discord_message ( message . channel , msg )
2018-12-10 18:44:31 +00:00
return
2018-12-09 16:22:52 +00:00
2018-09-20 02:56:17 +00:00
# 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 = " ----------------------------- " )
2018-12-10 21:28:32 +00:00
embed . add_field ( name = " {} register <username> " . format ( self . prefix ) , value = " Allows new members to join the Discord server if they have applied and been accepted. " )
2018-09-20 02:56:17 +00:00
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. " )
2018-12-06 19:25:26 +00:00
embed . add_field ( name = " {} demote <username> " . format ( self . prefix ) , value = " Demote a player to the role given to accepted applications. " )
2018-10-21 03:59:10 +00:00
embed . add_field ( name = " {} compare " . format ( self . prefix ) , value = " Compare Discord users to the Whitelist. " )
2018-09-20 02:56:17 +00:00
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 ( )
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 :
2018-12-09 05:30:56 +00:00
info + = " \n **This is only 10 applications out of {0} found. Please narrow your search if possible.** " . format (
len ( applications ) )
2018-09-20 02:56:17 +00:00
else :
2018-12-09 05:35:29 +00:00
players = Player . objects . filter ( username__icontains = search , application__isnull = False ) [ : 10 ]
count = Player . objects . filter ( username__icontains = search , application__isnull = False ) . count ( )
2018-12-09 05:30:56 +00:00
if count > 0 :
if count == 1 :
2018-12-09 05:38:30 +00:00
yield from self . discord_message ( message . channel , " **No applications matched, however there is a player match** " )
2018-12-09 05:30:56 +00:00
info = self . build_info ( players [ 0 ] . application )
else :
2018-12-09 05:38:30 +00:00
info = " **No applications matched, however there are player matches** "
2018-12-09 05:30:56 +00:00
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. "
2018-09-20 02:56:17 +00:00
yield from self . discord_message ( message . channel , info )
2018-10-21 03:59:10 +00:00
# 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 ) )
2018-09-20 02:56:17 +00:00
# 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 )