From 73844c0c8ff6c06e27485004b68d3ec605e590f0 Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Thu, 9 Aug 2018 21:25:55 -0500 Subject: [PATCH] Refactored how config data is handled and added new stress tests. --- Admin_Commands.py | 1 + BotConfig.py | 72 +++++++++++++++----------- Commands.py | 7 +-- DatabaseInterface.py | 7 ++- DatabaseModels.py | 8 +-- Delete_Commands.py | 3 ++ Geoffrey.py | 69 ++++++++++++++----------- test_commands.py | 9 +--- test_geoffreyDatabase.py | 106 +++++++++++++++++++-------------------- test_stress.py | 33 ++++++++++++ 10 files changed, 186 insertions(+), 129 deletions(-) create mode 100644 test_stress.py diff --git a/Admin_Commands.py b/Admin_Commands.py index 5600810..c3e2ec9 100644 --- a/Admin_Commands.py +++ b/Admin_Commands.py @@ -32,5 +32,6 @@ class Admin_Commands: else: raise NoPermissionError + def setup(bot): bot.add_cog(Admin_Commands(bot)) \ No newline at end of file diff --git a/BotConfig.py b/BotConfig.py index aebdef0..8c3ae8e 100644 --- a/BotConfig.py +++ b/BotConfig.py @@ -1,34 +1,50 @@ import configparser -def read_config(): - config = configparser.ConfigParser() - config.read('GeoffreyConfig.ini') - if len(config.sections()) == 0: - create_config(config) - print("GeoffreyConfig.ini generated.") - quit(0) +class Config: - return config + def __init__(self): + try: + self.config = self.read_config() + self.engine_args = self.read_engine_arg() + self.token = self.config['Discord']['Token'] + self.world_name = self.config['Minecraft']['World_Name'] + self.status = self.config['Discord']['Status'] + self.prefix = self.config['Discord']['Prefix'] + except: + print("Invalid config file") + quit(1) + + def read_config(self): + config = configparser.ConfigParser() + config.read('GeoffreyConfig.ini') + + if len(config.sections()) == 0: + self.create_config(config) + print("GeoffreyConfig.ini generated.") + quit(0) + + return config + + def create_config(self, config): + config['Discord'] = {'Token': '', 'Status': '', 'Prefix': '', } + config['SQL'] = {'Dialect+Driver': '', 'username': '', 'password': '', 'host': '', 'port': '', + 'database':'','test_args':''} + config['Minecraft'] = {'World_Name': ''} + with open('GeoffreyConfig.ini', 'w') as configfile: + config.write(configfile) + + def read_engine_arg(self): + driver = self.config['SQL']['Dialect+Driver'] + username = self.config['SQL']['username'] + password = self.config['SQL']['password'] + host = self.config['SQL']['host'] + port = self.config['SQL']['port'] + database_name = self.config['SQL']['database'] + + engine_args = '{}://{}:{}@{}:{}/{}?charset=utf8mb4&use_unicode=1' + + return engine_args.format(driver, username, password, host, port, database_name) -def create_config(config): - config['Discord'] = {'Token': ''} - config['SQL'] = {'Dialect+Driver': '', 'username': '', 'password':'', 'host': '', 'port': '', 'database':'', - 'test_args':''} - - with open('GeoffreyConfig.ini', 'w') as configfile: - config.write(configfile) - - -def get_engine_arg(config): - driver = config['SQL']['Dialect+Driver'] - username = config['SQL']['username'] - password = config['SQL']['password'] - host = config['SQL']['host'] - port = config['SQL']['port'] - database_name = config['SQL']['database'] - - engine_args = '{}://{}:{}@{}:{}/{}?charset=utf8mb4&use_unicode=1' - - return engine_args.format(driver, username, password, host, port, database_name) \ No newline at end of file +bot_config = Config() diff --git a/Commands.py b/Commands.py index 0b54d81..4ac2750 100644 --- a/Commands.py +++ b/Commands.py @@ -1,12 +1,9 @@ from DatabaseInterface import * -from itertools import zip_longest -from discord.ext.commands import UserInputError class Commands: - - def __init__(self, db_engine_arg): - self.interface = DatabaseInterface(db_engine_arg) + def __init__(self, engine_args=None): + self.interface = DatabaseInterface(engine_args) def get_player(self, session, discord_uuid=None, mc_uuid=None): if discord_uuid is not None: diff --git a/DatabaseInterface.py b/DatabaseInterface.py index 63077e5..96d3a31 100644 --- a/DatabaseInterface.py +++ b/DatabaseInterface.py @@ -3,8 +3,11 @@ from DatabaseModels import * class DatabaseInterface: - def __init__(self, db_engine_arg): - self.database = GeoffreyDatabase(db_engine_arg) + def __init__(self, engine_args=None): + if engine_args is None: + self.database = GeoffreyDatabase() + else: + self.database = GeoffreyDatabase(engine_args) def add_base(self, session, owner, name, x_pos, z_pos, dimension=None): base = Base(name, x_pos, z_pos, owner, dimension) diff --git a/DatabaseModels.py b/DatabaseModels.py index 202ad54..9af5c01 100644 --- a/DatabaseModels.py +++ b/DatabaseModels.py @@ -5,10 +5,10 @@ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.sql import expression import enum from difflib import SequenceMatcher +from BotConfig import bot_config from BotErrors import * from MinecraftAccountInfoGrabber import * -world_name = 'season3' SQL_Base = declarative_base() @@ -25,8 +25,8 @@ def check_similarity(a, b): class GeoffreyDatabase: - def __init__(self, engine_arg): - self.engine = create_engine(engine_arg, echo=True, pool_recycle=3600, pool_pre_ping=True) + def __init__(self, engine_args=bot_config.engine_args): + self.engine = create_engine(engine_args, echo=True, pool_recycle=3600, pool_pre_ping=True) self.Session = sessionmaker(bind=self.engine) SQL_Base.metadata.create_all(self.engine) @@ -207,7 +207,7 @@ class Location(SQL_Base): def dynmap_link(self): return ''.\ - format(world_name, self.x, self.z) + format(bot_config.world_name, self.x, self.z) def pos_to_str(self): return '(x= {}, z= {}) in the {}'.format(self.x, self.z, self.dimension.value.title()) diff --git a/Delete_Commands.py b/Delete_Commands.py index 272dce1..82a8b59 100644 --- a/Delete_Commands.py +++ b/Delete_Commands.py @@ -12,6 +12,9 @@ class Delete_Commands: *You must use ?register before using any of these commands!* ''' + def __init__(self, bot): + self.bot = bot + @commands.command(pass_context=True) async def delete(self, ctx, *args): ''' diff --git a/Geoffrey.py b/Geoffrey.py index e72bb13..cde2ad1 100644 --- a/Geoffrey.py +++ b/Geoffrey.py @@ -1,11 +1,11 @@ from discord.ext import commands +from discord import Game from Commands import * from BotErrors import * from MinecraftAccountInfoGrabber import * from BotConfig import * -import threading +import asyncio -command_prefix = '?' description = ''' Geoffrey (pronounced JOFF-ree) started his life as an inside joke none of you will understand. At some point, she was to become an airhorn bot. Now, they know where your stuff is. @@ -20,9 +20,9 @@ If have a suggestion or if something is borked, you can PM my ding dong of a cre bad_error_message = 'OOPSIE WOOPSIE!! Uwu We made a fucky wucky!! A wittle fucko boingo! The admins at our ' \ 'headquarters are working VEWY HAWD to fix this! (Error in command {}: {})' -bot = commands.Bot(command_prefix=command_prefix, description=description, case_insensitive=True) # Bot Commands ******************************************************************' +bot = commands.Bot(command_prefix=bot_config.prefix, description=description, case_insensitive=True) @bot.event @@ -31,6 +31,8 @@ async def on_ready(): print('Username: ' + bot.user.name) print('ID: ' + bot.user.id) + await bot.change_presence(game=Game(name=bot_config.status)) + @bot.event async def on_command_error(error, ctx): @@ -57,43 +59,54 @@ async def on_command_error(error, ctx): else: error_str = bad_error_message.format(ctx.invoked_with, error) - await bot.send_message(ctx.message.channel, '{} **Error Running Command:** {}'.format(ctx.message.author.mention, - error_str)) + bot.send_message(ctx.message.channel, '{} **Error Running Command:** {}'.format(ctx.message.author.mention, + error_str)) -def update_user_names(bot_commands): - threading.Timer(600, update_user_names, [bot_commands]).start() - session = bot_commands.interface.database.Session() - print("Updating MC usernames...") - player_list = session.query(Player).all() - - for player in player_list: - player.name = grab_playername(player.mc_uuid) - session.commit() +async def username_update(): + session = None + await bot.wait_until_ready() + while not bot.is_closed: + session = bot_commands.interface.database.Session() + try: + print("Updating MC usernames...") + session = bot_commands.interface.database.Session() + player_list = session.query(Player).all() + for player in player_list: + player.name = grab_playername(player.mc_uuid) - session.close() + session.commit() + + await asyncio.sleep(600) + + except UsernameLookupFailed: + print("Username lookup error, are Mojang's servers down?") + session.rollback() + finally: + print("Done.") + await session.close() + + if session is not None: + await session.close() # Bot Startup ****************************************************************** - - -config = read_config() - -TOKEN = config['Discord']['Token'] - -engine_arg = get_engine_arg(config) - -bot_commands = Commands(engine_arg) +bot_commands = Commands() extensions = ['Add_Commands', 'Delete_Commands', 'Edit_Commands', 'Search_Commands', 'Admin_Commands'] if __name__ == '__main__': + bot_commands = Commands() + for extension in extensions: try: bot.load_extension(extension) - except: - print('Failed to load extension {}'.format(extension)) + except Exception as e: + print('Failed to load extension {}, {}'.format(extension, e)) + + bot.loop.create_task(username_update()) + bot.run(bot_config.token) + + - update_user_names(bot_commands) - bot.run(TOKEN) diff --git a/test_commands.py b/test_commands.py index 2b853ac..3c89fbd 100644 --- a/test_commands.py +++ b/test_commands.py @@ -1,16 +1,13 @@ from unittest import TestCase -from BotConfig import * from Commands import * from BotErrors import * +from BotConfig import * class TestCommands(TestCase): def setUp(self): - config = read_config() - engine_arg = config['SQL']['test_args'] - - self.commands = Commands(engine_arg) + self.commands = Commands(bot_config.config['SQL']['test_args']) self.session = self.commands.interface.database.Session() self.commands.interface.database.clear_all(self.session) self.session.close() @@ -225,5 +222,3 @@ class TestCommands(TestCase): self.assertRaises(LocationLookUpError, self.commands.delete_item, 'wood', None, discord_uuid='143072699567177728') - - diff --git a/test_geoffreyDatabase.py b/test_geoffreyDatabase.py index 5042d8b..bdc394d 100644 --- a/test_geoffreyDatabase.py +++ b/test_geoffreyDatabase.py @@ -2,18 +2,15 @@ from unittest import TestCase from Commands import * from BotErrors import * from MinecraftAccountInfoGrabber import * -from BotConfig import * +from BotConfig import bot_config class TestGeoffreyDatabase(TestCase): def setUp(self): - config = read_config() + self.interface = DatabaseInterface(bot_config.config['SQL']['test_args']) - engine_arg = config['SQL']['test_args'] - self.commands = Commands(engine_arg) - - self.session = self.commands.interface.database.Session() - self.commands.interface.database.clear_all(self.session) + self.session = self.interface.database.Session() + self.interface.database.clear_all(self.session) self.owner = Player('ZeroHD', '143072699567177728') self.loc = Location('test', 1, 3, self.owner, dimension='Nether') self.tunnel = Tunnel(self.owner, 'Green', 105, self.loc) @@ -23,57 +20,57 @@ class TestGeoffreyDatabase(TestCase): self.session.close() def add_shop(self, player): - shop = self.commands.interface.add_shop(self.session, player, 'test', 1, 3, "nether") + shop = self.interface.add_shop(self.session, player, 'test', 1, 3, "nether") return shop def add_player(self): - player = self.commands.interface.add_player(self.session, 'ZeroHD', discord_uuid='143072699567177728') + player = self.interface.add_player(self.session, 'ZeroHD', discord_uuid='143072699567177728') return player def add_loc(self, player): - loc = self.commands.interface.add_base(self.session, player, 'test', 0, 0) + loc = self.interface.add_base(self.session, player, 'test', 0, 0) return loc def test_add_object(self): - self.commands.interface.database.add_object(self.session, self.loc) - self.commands.interface.database.add_object(self.session, self.owner) - self.commands.interface.database.add_object(self.session, self.tunnel) + self.interface.database.add_object(self.session, self.loc) + self.interface.database.add_object(self.session, self.owner) + self.interface.database.add_object(self.session, self.tunnel) uuid = grab_UUID('ZeroHD') expr = Player.mc_uuid == uuid - p = self.commands.interface.database.query_by_filter(self.session, Player, expr)[0] + p = self.interface.database.query_by_filter(self.session, Player, expr)[0] expr = Location.owner == p - loc2 = self.commands.interface.database.query_by_filter(self.session, Location, expr)[0] + loc2 = self.interface.database.query_by_filter(self.session, Location, expr)[0] self.assertEqual(self.loc.id, loc2.id) def test_query_by_filter(self): - self.commands.interface.database.add_object(self.session, self.loc) - self.commands.interface.database.add_object(self.session, self.owner) + self.interface.database.add_object(self.session, self.loc) + self.interface.database.add_object(self.session, self.owner) expr = (Location.owner == self.owner) - loc2 = self.commands.interface.database.query_by_filter(self.session, Location, expr)[0] + loc2 = self.interface.database.query_by_filter(self.session, Location, expr)[0] self.assertEqual(loc2.id, self.loc.id) def test_delete_entry(self): - self.commands.interface.database.add_object(self.session, self.loc) - self.commands.interface.database.add_object(self.session, self.owner) + self.interface.database.add_object(self.session, self.loc) + self.interface.database.add_object(self.session, self.owner) self.session.commit() - id = self.loc.owner_id + self.loc.owner_id expr = Location.owner == self.owner - self.commands.interface.database.delete_entry(self.session, Location, expr) + self.interface.database.delete_entry(self.session, Location, expr) expr = Player.name == 'ZeroHD' - player = self.commands.interface.database.query_by_filter(self.session, Player, expr)[0] + player = self.interface.database.query_by_filter(self.session, Player, expr)[0] self.assertEqual(player.name, 'ZeroHD') expr = Location.owner == player - loc2 = self.commands.interface.database.query_by_filter(self.session, Location, expr) + loc2 = self.interface.database.query_by_filter(self.session, Location, expr) self.assertEqual(len(loc2), 0) - self.assertRaises(DeleteEntryError, self.commands.interface.database.delete_entry, self.session, Location, expr) + self.assertRaises(DeleteEntryError, self.interface.database.delete_entry, self.session, Location, expr) def test_add_shop(self): owner = self.add_player() @@ -81,40 +78,39 @@ class TestGeoffreyDatabase(TestCase): self.assertEqual(type(shop), Shop) - shop_list = self.commands.interface.find_shop_by_name(self.session, 'test') + shop_list = self.interface.find_shop_by_name(self.session, 'test') self.assertEqual(shop_list[0].dimension, shop.dimension) def test_add_two_shops(self): owner = self.add_player() shop1 = self.add_shop(owner) - shop2 = self.commands.interface.add_shop(self.session, owner, 'no u', 1, 3) + shop2 = self.interface.add_shop(self.session, owner, 'no u', 1, 3) - loc_list = self.commands.interface.find_location_by_owner(self.session, owner) + loc_list = self.interface.find_location_by_owner(self.session, owner) self.assertEqual(loc_list[1].id, shop2.id) def test_add_tunnel(self): player = self.add_player() - tunnel1 = self.commands.interface.add_tunnel(self.session, player, 'green', 155, None) + tunnel1 = self.interface.add_tunnel(self.session, player, 'green', 155, None) - tunnel2 = self.commands.interface.find_tunnel_by_owner_name(self.session, 'ZeroHD')[0] + tunnel2 = self.interface.find_tunnel_by_owner_name(self.session, 'ZeroHD')[0] self.assertEqual(tunnel1, tunnel2) - def test_add_item(self): owner = self.add_player() self.add_shop(owner) - self.commands.interface.add_item(self.session, owner, 'test', 'dirt', 1, 15) + self.interface.add_item(self.session, owner, 'test', 'dirt', 1, 15) - shops = self.commands.interface.find_shop_selling_item(self.session, 'dirt') + shops = self.interface.find_shop_selling_item(self.session, 'dirt') self.assertGreater(len(shops), 0) def test_find_location_by_owner(self): owner = self.add_player() shop = self.add_shop(owner) - loc_list = self.commands.interface.find_location_by_owner(self.session, owner) + loc_list = self.interface.find_location_by_owner(self.session, owner) self.assertEqual(loc_list[0].id, shop.id) @@ -122,7 +118,7 @@ class TestGeoffreyDatabase(TestCase): owner = self.add_player() shop = self.add_shop(owner) - loc_list = self.commands.interface.find_location_by_name_and_owner(self.session, owner, 'test') + loc_list = self.interface.find_location_by_name_and_owner(self.session, owner, 'test') self.assertEqual(loc_list[0].id, shop.id) @@ -130,9 +126,9 @@ class TestGeoffreyDatabase(TestCase): owner = self.add_player() self.add_loc(owner) - self.commands.interface.delete_location(self.session, owner, 'test') + self.interface.delete_location(self.session, owner, 'test') - loc_list = self.commands.interface.find_location_by_name(self.session, 'test') + loc_list = self.interface.find_location_by_name(self.session, 'test') self.assertEqual(len(loc_list), 0) @@ -142,27 +138,27 @@ class TestGeoffreyDatabase(TestCase): dim = "o" - loc_list = self.commands.interface.find_location_around(self.session, 100, 100, 100, dim) + loc_list = self.interface.find_location_around(self.session, 100, 100, 100, dim) self.assertGreater(len(loc_list), 0) - loc_list = self.commands.interface.find_location_around(self.session, 200, 200, 100, dim) + loc_list = self.interface.find_location_around(self.session, 200, 200, 100, dim) self.assertEqual(len(loc_list), 0) - loc_list = self.commands.interface.find_location_around(self.session, -100, -100, 100, dim) + loc_list = self.interface.find_location_around(self.session, -100, -100, 100, dim) self.assertGreater(len(loc_list), 0) - loc_list = self.commands.interface.find_location_around(self.session, 100, -100, 100, dim) + loc_list = self.interface.find_location_around(self.session, 100, -100, 100, dim) self.assertGreater(len(loc_list), 0) - loc_list = self.commands.interface.find_location_around(self.session, -100, 100, 100, dim) + loc_list = self.interface.find_location_around(self.session, -100, 100, 100, dim) self.assertGreater(len(loc_list), 0) - loc_list = self.commands.interface.find_location_around(self.session, 50, -50, 100, dim) + loc_list = self.interface.find_location_around(self.session, 50, -50, 100, dim) self.assertGreater(len(loc_list), 0) @@ -170,7 +166,7 @@ class TestGeoffreyDatabase(TestCase): owner = self.add_player() loc = self.add_loc(owner) - loc_list = self.commands.interface.find_location_by_name(self.session, 'test') + loc_list = self.interface.find_location_by_name(self.session, 'test') self.assertEqual(loc_list[0].name, loc.name) @@ -178,7 +174,7 @@ class TestGeoffreyDatabase(TestCase): owner = self.add_player() loc = self.add_loc(owner) - loc_list = self.commands.interface.search_all_fields(self.session, 'ZeroHD') + loc_list = self.interface.search_all_fields(self.session, 'ZeroHD') self.assertGreater(len(loc_list), 0) @@ -186,26 +182,26 @@ class TestGeoffreyDatabase(TestCase): owner = self.add_player() loc = self.add_loc(owner) - loc_list = self.commands.interface.find_location_by_owner_name(self.session, 'zerohd') + loc_list = self.interface.find_location_by_owner_name(self.session, 'zerohd') self.assertEqual(loc_list[0].id, loc.id) - self.commands.interface.add_shop(self.session, owner, 'testshop', 1, 3, 'neThEr') + self.interface.add_shop(self.session, owner, 'testshop', 1, 3, 'neThEr') - self.commands.interface.add_item(self.session, owner, 'testshop', 'dirts', 1, 15) + self.interface.add_item(self.session, owner, 'testshop', 'dirts', 1, 15) - shops = self.commands.interface.find_shop_selling_item(self.session, 'diRt') + shops = self.interface.find_shop_selling_item(self.session, 'diRt') self.assertGreater(len(shops), 0) - loc_list = self.commands.interface.find_location_by_name(self.session, 'TEST') + loc_list = self.interface.find_location_by_name(self.session, 'TEST') self.assertEqual(loc_list[0].name, 'test') def test_big_input(self): owner = self.add_player() - self.assertRaises(DatabaseValueError, self.commands.interface.add_base, self.session, owner, + self.assertRaises(DatabaseValueError, self.interface.add_base, self.session, owner, 'TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT' 'TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT' 'TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT', 0, 0,) @@ -214,18 +210,18 @@ class TestGeoffreyDatabase(TestCase): owner = self.add_player() self.add_loc(owner) - self.assertRaises(EntryNameNotUniqueError, self.commands.interface.add_base, self.session, + self.assertRaises(EntryNameNotUniqueError, self.interface.add_base, self.session, owner, 'test', 0, 0, 0) def test_delete_parent(self): owner = self.add_player() loc = self.add_shop(owner) - self.commands.interface.add_item(self.session, owner, 'test', 'dirt', 1, 15) + self.interface.add_item(self.session, owner, 'test', 'dirt', 1, 15) - self.commands.interface.delete_location(self.session, owner, 'test') + self.interface.delete_location(self.session, owner, 'test') - shops = self.commands.interface.find_shop_selling_item(self.session, 'dirt') + shops = self.interface.find_shop_selling_item(self.session, 'dirt') self.assertEqual(len(shops), 0) diff --git a/test_stress.py b/test_stress.py new file mode 100644 index 0000000..cc885be --- /dev/null +++ b/test_stress.py @@ -0,0 +1,33 @@ +from unittest import TestCase +from Commands import * +from BotConfig import * +from MinecraftAccountInfoGrabber import * +from time import sleep + + +class StressTest(TestCase): + def setUp(self): + self.commands = Commands(bot_config.config['SQL']['test_args']) + + def clr_db(self): + self.session = self.commands.interface.database.Session() + self.commands.interface.database.clear_all(self.session) + self.session.close() + + def test_commands(self): + self.clr_db() + self.commands.register('ZeroHD', '143072699567177728') + + for i in range(0, 1000): + self.commands.add_shop(0, 0, shop_name='test shop{}'.format(i), discord_uuid='143072699567177728') + + self.commands.find('ZeroHD') + + sleep(0.5) + + def test_mc_query(self): + + for i in range(0, 1000): + grab_playername('fe7e84132570458892032b69ff188bc3') + + sleep(0.1)