From 3bb1e910c9409b9302a91aede8bf3e10af86143b Mon Sep 17 00:00:00 2001 From: Etz Elia Date: Tue, 14 Apr 2020 21:36:31 +0200 Subject: [PATCH] Make discord bot external again (#48) Make discord bot external again Signed-off-by: Etzelia Co-authored-by: Etzelia Reviewed-by: ZeroHD --- api/api.py | 28 +++++--- bot/__init__.py | 5 +- bot/assets/Discord-MCM.bot.py | 21 ++++++ bot/discord.py | 118 ++++---------------------------- docs/source/django-settings.rst | 4 ++ views.py | 4 +- 6 files changed, 62 insertions(+), 118 deletions(-) create mode 100644 bot/assets/Discord-MCM.bot.py diff --git a/api/api.py b/api/api.py index 98ea7f2..c194ca5 100644 --- a/api/api.py +++ b/api/api.py @@ -1,8 +1,7 @@ -import socket, logging, os, datetime, pytz, mcstatus, random, string +import socket, requests, logging, os, datetime, pytz, mcstatus, random, string from minecraft_manager.models import Alert from django.contrib.auth.models import User from django.conf import settings -from minecraft_manager.bot.discord import send as discord_send, DestType logger = logging.getLogger(__name__) @@ -39,24 +38,35 @@ def plugin(key, command): def discord_mcm(message='', embed=None, ping=False): - channel_id = getattr(settings, 'DISCORD_MCM_CHANNEL', None) - if channel_id: + 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) - discord_send(DestType.CHANNEL, channel_id, message, embed) + data = {} + if message: + data['content'] = message + if embed: + data['embeds'] = embed + return requests.post(discord_mcm_webhook, json=data) + return None def discord_notification(message='', embed=None, ping=False): - channel_id = getattr(settings, 'DISCORD_NOTIFICATION_CHANNEL', None) - if channel_id: + 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) - discord_send(DestType.CHANNEL, channel_id, message, embed) - + data = {} + if message: + data['content'] = message + if embed: + data['embeds'] = embed + return requests.post(discord_notification_webhook, json=data) + return None def strip_format(message): diff --git a/bot/__init__.py b/bot/__init__.py index 9e52327..0d0d377 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -1,5 +1,5 @@ from django.conf import settings -import subprocess +import subprocess, os class Bot: @@ -17,10 +17,11 @@ class Bot: self.restart = restart if restart else self._restart self.status = status if status else self._status self.display = display if display else self._display + self.dir = "{}/assets/".format(os.path.dirname(os.path.abspath(__file__))) if self.asset else self.bot_dir def _start(self): screen = 'screen -S {0}_{1} -d -m {2} {3}{1}.bot.py' - subprocess.run(screen.format(self.plugin_port, self.name, self.executable, self.bot_dir), + subprocess.run(screen.format(self.plugin_port, self.name, self.executable, self.dir), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) def _stop(self): diff --git a/bot/assets/Discord-MCM.bot.py b/bot/assets/Discord-MCM.bot.py new file mode 100644 index 0000000..092a99e --- /dev/null +++ b/bot/assets/Discord-MCM.bot.py @@ -0,0 +1,21 @@ +import os +import sys +import django + +sep = os.sep +path = os.path.dirname(os.path.abspath(__file__)) +path = path.split(sep)[:-3] +project = path[-1] +path = sep.join(path) +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.bot.discord import Discord + +token = getattr(settings, 'DISCORD_BOT_TOKEN', None) +bot = Discord(token) + +bot.run_bot() diff --git a/bot/discord.py b/bot/discord.py index 993cc37..54a8087 100644 --- a/bot/discord.py +++ b/bot/discord.py @@ -13,29 +13,11 @@ from minecraft_manager.utils import url_path logger = logging.getLogger(__name__) -discord_loop = None -discord_bot = None description = ''' A Discord bot connected to an MCM instance. ''' -class DiscordStatus(Enum): - STOPPED = 0 - STARTING = 1 - STARTED = 2 - STOPPING = 3 - - def __str__(self): - return self.name.title() - - def is_running(self): - return self != DiscordStatus.STOPPED - - -discord_status = DiscordStatus.STOPPED - - class Discord(commands.Bot): discord_game = 'MCM' prefix = getattr(settings, 'DISCORD_BOT_PREFIX', '!') @@ -43,14 +25,13 @@ class Discord(commands.Bot): superuser_roles = getattr(settings, 'DISCORD_SUPERUSER_ROLES', []) error_users = getattr(settings, 'DISCORD_ERROR_USERS', []) - def __init__(self, token, loop): - super().__init__(command_prefix=self.prefix, description=description, case_insensitive=True, help_command=None, activity=discord.Game(name=self.discord_game), loop=loop) + 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): - global discord_status - discord_status = DiscordStatus.STARTED print('Logged in as') print(self.user.name) print(self.user.id) @@ -80,10 +61,6 @@ class Discord(commands.Bot): embed.description = content await self.discord_message(channel, embed) - async def on_disconnect(self): - global discord_status - discord_status = DiscordStatus.STOPPED - async def discord_message(self, dest, message): if isinstance(message, discord.Embed): for idx, field in enumerate(message.fields): @@ -107,81 +84,14 @@ class Discord(commands.Bot): await self.discord_message(user, '```python\n{}```'.format(error)) def run_bot(self): - global discord_loop - loop = True - while loop: - try: - discord_loop.run_until_complete(self.start(self.token)) - except KeyboardInterrupt: - logger.info("Bot received keyboard interrupt") - loop = False - except Exception as e: - print(e) - logger.info('Bot encountered the following unhandled exception %s', e) - loop = False - - -def start(): - global discord_loop, discord_bot, discord_status - if discord_status != DiscordStatus.STOPPED: - return - token = getattr(settings, 'DISCORD_BOT_TOKEN', None) - discord_loop = asyncio.new_event_loop() - discord_bot = Discord(token, discord_loop) - thread = threading.Thread(target=discord_bot.run_bot) - thread.start() - discord_status = DiscordStatus.STARTING - - -def stop(): - global discord_loop, discord_bot, discord_status - if discord_status == DiscordStatus.STARTED: - discord_loop.create_task(discord_bot.close()) - discord_status = DiscordStatus.STOPPING - discord_loop = None - discord_bot = None - - -def restart(): - def _restart(): - stop() - while discord_status.is_running(): - pass - start() - if discord_status != DiscordStatus.STARTED: - return - thread = threading.Thread(target=_restart) - thread.start() - - -def status(): - return discord_status.is_running() - - -def display(): - return str(discord_status) - - -class DestType(Enum): - CHANNEL = 1 - USER = 2 - - -def send(dest_type: DestType, dest_id: int, message: str = "", embed: discord.Embed = None): - async def _send(): - if dest_type == DestType.CHANNEL: - dest = discord_bot.get_channel(dest_id) - elif dest_type == DestType.USER: - dest = discord_bot.get_user(dest_id) - else: - return - if message is not None: - await dest.send(message) - if embed is not None: - for idx, field in enumerate(embed.fields): - if not field.value: - embed.set_field_at(idx, name=field.name, value="N/A") - await dest.send(embed=embed) - global discord_loop, discord_bot - if discord_loop: - discord_loop.create_task(_send()) + 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...") diff --git a/docs/source/django-settings.rst b/docs/source/django-settings.rst index 5ef0bea..76a6469 100644 --- a/docs/source/django-settings.rst +++ b/docs/source/django-settings.rst @@ -23,6 +23,10 @@ Optional ``BOT_DIR`` - The path to your bot directory. +``DISCORD_NOTIFICATION_WEBHOOK`` - The URL for the webhook used for notifications. + +``DISCORD_MCM_WEBHOOK`` - The URL for the webhook used for Applications, Tickets, and Warnings. + ``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. diff --git a/views.py b/views.py index 845e129..5dc5c61 100644 --- a/views.py +++ b/views.py @@ -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 from minecraft_manager.bot import Bot -from minecraft_manager.bot.discord import start as discord_start, stop as discord_stop, restart as discord_restart, \ - status as discord_status, display as discord_display class Overview(View): @@ -467,7 +465,7 @@ class Bots(View): bots.append(Bot(file.replace('.bot.py', ''), False, sys.executable)) # Also get packaged MCM bots if getattr(settings, 'DISCORD_BOT_TOKEN', None): - bots.append(Bot("Discord-MCM", True, None, discord_start, discord_stop, discord_restart, discord_status, discord_display)) + bots.append(Bot("Discord-MCM", True, sys.executable)) return bots def get(self, request):