From 4eddf2aabdf5ddb11bfad570ae96423953ac1c6f Mon Sep 17 00:00:00 2001 From: Dustin Pianalto Date: Fri, 4 May 2018 14:19:44 -0800 Subject: [PATCH] Initial Commit --- .gitignore | 1 + .idea/dictionaries/Dustin_Pianalto.xml | 16 +++ bot.py | 148 +++++++++++++++++++++++++ config/bot_config.json | 6 + exts/__init__.py | 0 exts/admin.py | 3 + exts/imports/__init__.py | 0 exts/imports/utils.py | 83 ++++++++++++++ exts/uploader.py | 24 ++++ exts/utils.py | 3 + 10 files changed, 284 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/dictionaries/Dustin_Pianalto.xml create mode 100644 bot.py create mode 100644 config/bot_config.json create mode 100644 exts/__init__.py create mode 100644 exts/admin.py create mode 100644 exts/imports/__init__.py create mode 100644 exts/imports/utils.py create mode 100644 exts/uploader.py create mode 100644 exts/utils.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..786e996 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +bot_secrets.json diff --git a/.idea/dictionaries/Dustin_Pianalto.xml b/.idea/dictionaries/Dustin_Pianalto.xml new file mode 100644 index 0000000..21a19af --- /dev/null +++ b/.idea/dictionaries/Dustin_Pianalto.xml @@ -0,0 +1,16 @@ + + + + aiohttp + asctime + chans + coro + customsearch + emojis + exts + levelname + lockdown + msecs + + + \ No newline at end of file diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..8abfcd6 --- /dev/null +++ b/bot.py @@ -0,0 +1,148 @@ +import discord +from discord.ext import commands +import logging +import json +import aiohttp +import asyncpg +from concurrent import futures +from typing import Dict + +log_format = '{asctime}.{msecs:03.0f}|{levelname:<8}|{name}::{message}' +date_format = '%Y.%m.%d %H.%M.%S' + +log_dir = 'logs' + +log_file = '{0}/submitter_{1}.log'.format(log_dir, datetime.now().strftime('%Y%m%d_%H%M%S%f')) + +logging.basicConfig(level=logging.DEBUG, style='{', filename=log_file, datefmt=date_format, format=log_format) +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.INFO) +formatter = logging.Formatter(log_format, style='{', datefmt=date_format) +console_handler.setFormatter(formatter) +logging.getLogger('').addHandler(console_handler) + +extension_dir = 'exts' +owner_id = 351794468870946827 +bot_config_file = 'bot_config.json' +secrets_file = 'bot_secrets.json' + +description = 'Submission Bot for Ark Smart Breeder' + + +class Submitter(commands.Bot): + def __init__(self, **kwargs): + kwargs["command_prefix"] = self.get_custom_prefix + super().__init__(**kwargs) + self.aio_session = aiohttp.ClientSession(loop=self.loop) + with open(f'{config_dir}{bot_config_file}') as file: + self.bot_config = json.load(file) + with open(f'{config_dir}{secrets_file}') as file: + self.bot_secrets = json.load(file) + self.guild_config = {} + self.infected = {} + self.TOKEN = self.bot_secrets['token'] + del self.bot_secrets['token'] + self.db_con = None + self.default_prefix = '!' + self.tpe = futures.ThreadPoolExecutor() + self.unicode_emojis: Dict[str, str] = { + 'x': '❌', + 'y': '✅', + 'poop': '💩', + 'boom': '💥', + 'left_fist': '🤛', + } + + async def connect_db(self): + self.db_con = await asyncpg.create_pool(host=self.bot_secrets['db_con']['host'], + database=self.bot_secrets['db_con']['db_name'], + user=self.bot_secrets['db_con']['user'], + password=self.bot_secrets['db_con']['password'], + loop=self.loop) + + @staticmethod + async def get_custom_prefix(bot_inst, message): + return await bot_inst.db_con.fetchval('select prefix from guild_config where guild_id = $1', + message.guild.id) or bot_inst.default_prefix + + async def load_ext(self, ctx, mod=None): + self.load_extension('{0}.{1}'.format(extension_dir, mod)) + if ctx is not None: + await ctx.send('{0} loaded.'.format(mod)) + + async def unload_ext(self, ctx, mod=None): + self.unload_extension('{0}.{1}'.format(extension_dir, mod)) + if ctx is not None: + await ctx.send('{0} unloaded.'.format(mod)) + + async def close(self): + await super().close() + self.aio_session.close() # aiohttp is drunk and can't decide if it's a coro or not + + +bot = Submitter(description=description, case_insensitive=True) + + +@bot.command(hidden=True) +@commands.is_owner() +async def load(ctx, mod=None): + """Allows the owner to load extensions dynamically""" + await bot.load_ext(ctx, mod) + + +@bot.command(hidden=True) +@commands.is_owner() +async def reload(ctx, mod=None): + """Allows the owner to reload extensions dynamically""" + if mod == 'all': + load_list = bot.bot_config['load_list'] + for load_item in load_list: + await bot.unload_ext(ctx, f'{load_item}') + await bot.load_ext(ctx, f'{load_item}') + else: + await bot.unload_ext(ctx, mod) + await bot.load_ext(ctx, mod) + + +@bot.command(hidden=True) +@commands.is_owner() +async def unload(ctx, mod): + """Allows the owner to unload extensions dynamically""" + await bot.unload_ext(ctx, mod) + + +@bot.event +async def on_message(ctx): + if not ctx.author.bot: + if ctx.guild: + if int(await bot.db_con.fetchval("select channel_lockdown from guild_config where guild_id = $1", + ctx.guild.id)): + if ctx.channel.id in json.loads(await bot.db_con.fetchval("select allowed_channels from guild_config " + "where guild_id = $1", + ctx.guild.id)): + await bot.process_commands(ctx) + else: + await bot.process_commands(ctx) + else: + await bot.process_commands(ctx) + + +@bot.event +async def on_ready(): + if bot.db_con is None: + await bot.connect_db() + bot.recent_msgs = {} + logging.info('Logged in as {0.name}|{0.id}'.format(bot.user)) + load_list = bot.bot_config['load_list'] + for load_item in load_list: + await bot.load_ext(None, f'{load_item}') + logging.info('Extension Loaded: {0}'.format(load_item)) + with open(f'{config_dir}reboot', 'r') as f: + reboot = f.readlines() + if int(reboot[0]) == 1: + await bot.get_channel(int(reboot[1])).send('Restart Finished.') + with open(f'{config_dir}reboot', 'w') as f: + f.write(f'0') + logging.info('Done loading, Submitter is active.') + +bot.run(bot.TOKEN) diff --git a/config/bot_config.json b/config/bot_config.json new file mode 100644 index 0000000..c945089 --- /dev/null +++ b/config/bot_config.json @@ -0,0 +1,6 @@ +{ + "load_list": [ + "admin", + "uploader" + ] +} diff --git a/exts/__init__.py b/exts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/exts/admin.py b/exts/admin.py new file mode 100644 index 0000000..4f1212a --- /dev/null +++ b/exts/admin.py @@ -0,0 +1,3 @@ +import discord +from discord.ext import commands +import os \ No newline at end of file diff --git a/exts/imports/__init__.py b/exts/imports/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/exts/imports/utils.py b/exts/imports/utils.py new file mode 100644 index 0000000..dee633d --- /dev/null +++ b/exts/imports/utils.py @@ -0,0 +1,83 @@ +from io import StringIO +import sys +import asyncio +import discord +from discord.ext.commands.formatter import Paginator + + +class Capturing(list): + def __enter__(self): + self._stdout = sys.stdout + sys.stdout = self._stringio = StringIO() + return self + + def __exit__(self, *args): + self.extend(self._stringio.getvalue().splitlines()) + del self._stringio # free up some memory + sys.stdout = self._stdout + + +def to_list_of_str(items, out: list=list(), level=1, recurse=0): + def rec_loop(item, key, out, level): + quote = '"' + if type(item) == list: + out.append(f'{" "*level}{quote+key+quote+": " if key else ""}[') + new_level = level + 1 + out = to_list_of_str(item, out, new_level, 1) + out.append(f'{" "*level}]') + elif type(item) == dict: + out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{{') + new_level = level + 1 + out = to_list_of_str(item, out, new_level, 1) + out.append(f'{" "*level}}}') + else: + out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{repr(item)},') + + if type(items) == list: + if not recurse: + out = list() + out.append('[') + for item in items: + rec_loop(item, None, out, level) + if not recurse: + out.append(']') + elif type(items) == dict: + if not recurse: + out = list() + out.append('{') + for key in items: + rec_loop(items[key], key, out, level) + if not recurse: + out.append('}') + + return out + + +def paginate(text, maxlen=1990): + paginator = Paginator(prefix='```py', max_size=maxlen+10) + if type(text) == list: + data = to_list_of_str(text) + elif type(text) == dict: + data = to_list_of_str(text) + else: + data = str(text).split('\n') + for line in data: + if len(line) > maxlen: + n = maxlen + for l in [line[i:i+n] for i in range(0, len(line), n)]: + paginator.add_line(l) + else: + paginator.add_line(line) + return paginator.pages + + +async def run_command(args): + # Create subprocess + process = await asyncio.create_subprocess_shell( + args, + # stdout must a pipe to be accessible as process.stdout + stdout=asyncio.subprocess.PIPE) + # Wait for the subprocess to finish + stdout, stderr = await process.communicate() + # Return stdout + return stdout.decode().strip() diff --git a/exts/uploader.py b/exts/uploader.py new file mode 100644 index 0000000..4f645a3 --- /dev/null +++ b/exts/uploader.py @@ -0,0 +1,24 @@ +import discord +from discord.ext import commands +import zipfile +import os + + +class Uploader: + def __init__(self, bot): + self.bot = bot + + @commands.command(name='submit', aliases=['upload']) + async def upload_dino(self, ctx, official: str='unofficial'): + if official == 'unofficial': + + with zipfile.ZipFile('archive.zip') as z: + for filename in z.namelist(): + if not os.path.isdir(filename): + with z.open(filename) as f: + for line in f: + print(line) + + +def setup(bot): + bot.add_cog(Uploader(bot)) diff --git a/exts/utils.py b/exts/utils.py new file mode 100644 index 0000000..4f1212a --- /dev/null +++ b/exts/utils.py @@ -0,0 +1,3 @@ +import discord +from discord.ext import commands +import os \ No newline at end of file