diff --git a/DatabaseModels.py b/DatabaseModels.py index 8d3460d..c49c200 100644 --- a/DatabaseModels.py +++ b/DatabaseModels.py @@ -7,22 +7,30 @@ from sqlalchemy.orm import sessionmaker, relationship from sqlalchemy.exc import IntegrityError import sqlalchemy from MinecraftAccountInfoGrabber import * +from difflib import SequenceMatcher SQL_Base = declarative_base() +def check_similarity(a, b): + ratio = SequenceMatcher(None, a, b).ratio() + + if (ratio > 0.6) or (a[0] == b[0]): + return True + else: + return False class DatabaseInterface: def __init__(self, db_engine_arg): self.database = GeoffreyDatabase(db_engine_arg) - def add_location(self, owner, name, x_pos, y_pos, z_pos, args): - location = Location(name, x_pos, y_pos, z_pos, owner, args) + def add_location(self, owner, name, x_pos, y_pos, z_pos, tunnel=None, dimension=None): + location = Location(name, x_pos, y_pos, z_pos, owner, tunnel, dimension) self.database.add_object(location) return location - def add_shop(self, owner, name, x_pos, y_pos, z_pos, args): - shop = Shop(name, x_pos, y_pos, z_pos, owner, args) + def add_shop(self, owner, name, x_pos, y_pos, z_pos, tunnel=None, dimension=None): + shop = Shop(name, x_pos, y_pos, z_pos, owner, tunnel, dimension) self.database.add_object(shop) return shop @@ -79,8 +87,9 @@ class DatabaseInterface: return self.database.query_by_filter(Location, expr) def find_location_around(self, x_pos, z_pos, radius, dimension): + dimension_obj = Dimension.str_to_dimension(dimension) expr = (Location.x < x_pos + radius + 1) & (Location.x > x_pos - radius - 1) & (Location.z < z_pos + radius + 1) \ - & (Location.z > z_pos - radius - 1) & (Location.dimension == dimension) + & (Location.z > z_pos - radius - 1) & (Location.dimension == dimension_obj) return self.database.query_by_filter(Location, expr) @@ -140,13 +149,13 @@ class DatabaseInterface: class DiscordDatabaseInterface(DatabaseInterface): - def add_location(self, owner_uuid, name, x_pos, y_pos, z_pos, args): + def add_location(self, owner_uuid, name, x_pos, y_pos, z_pos, tunnel=None, dimension=None): owner = DatabaseInterface.find_player_by_discord_uuid(self, owner_uuid) - return DatabaseInterface.add_location(self, owner, name, x_pos, y_pos, z_pos, args) + return DatabaseInterface.add_location(self, owner, name, x_pos, y_pos, z_pos, tunnel, dimension) - def add_shop(self, owner_uuid, name, x_pos, y_pos, z_pos, args): + def add_shop(self, owner_uuid, name, x_pos, y_pos, z_pos, tunnel=None, dimension=None): owner = DatabaseInterface.find_player_by_discord_uuid(self, owner_uuid) - return DatabaseInterface.add_shop(self, owner, name, x_pos, y_pos, z_pos, args) + return DatabaseInterface.add_shop(self, owner, name, x_pos, y_pos, z_pos, tunnel, dimension) def add_item(self, owner_uuid, shop_name, item_name, price, amount): owner = DatabaseInterface.find_player_by_discord_uuid(self, owner_uuid) @@ -236,13 +245,14 @@ class TunnelDirection(enum.Enum): def str_to_tunnel_dir(arg): arg = arg.lower() - if arg in TunnelDirection.North.value: + + if check_similarity(TunnelDirection.North.value, arg): return TunnelDirection.North - elif arg in TunnelDirection.East.value: + elif check_similarity(TunnelDirection.East.value, arg): return TunnelDirection.East - elif arg in TunnelDirection.South.value: + elif check_similarity(TunnelDirection.South.value, arg): return TunnelDirection.South - elif arg in TunnelDirection.West.value: + elif check_similarity(TunnelDirection.West.value, arg): return TunnelDirection.West else: raise ValueError @@ -254,9 +264,9 @@ class TunnelSide(enum.Enum): def str_to_tunnel_side(arg): arg = arg.lower() - if arg in TunnelSide.right.value: + if check_similarity(TunnelSide.right.value, arg): return TunnelSide.right - elif arg in TunnelSide.left.value: + elif check_similarity(TunnelSide.left.value, arg): return TunnelSide.left else: raise ValueError @@ -269,11 +279,11 @@ class Dimension(enum.Enum): def str_to_dimension(arg): arg = arg.lower() - if arg in Dimension.overworld.value: + if check_similarity(Dimension.overworld.value, arg): return Dimension.overworld - elif arg in Dimension.nether.value: + elif check_similarity(Dimension.nether.value, arg): return Dimension.nether - elif arg in Dimension.end.value: + elif check_similarity(Dimension.end.value, arg): return Dimension.end else: raise ValueError @@ -301,8 +311,8 @@ class Location(SQL_Base): x = Column(Integer) y = Column(Integer) z = Column(Integer) - tunnelNumber = Column(Integer) - direction = Column(Enum(TunnelDirection)) + tunnel_number = Column(Integer) + tunnel_direction = Column(Enum(TunnelDirection)) tunnel_side = Column(Enum(TunnelSide)) dimension = Column(Enum(Dimension)) @@ -315,7 +325,7 @@ class Location(SQL_Base): 'polymorphic_identity': 'Location' } - def __init__(self, name, x, y, z, owner, args): + def __init__(self, name, x, y, z, owner, tunnel, dimension): try: self.name = name self.x = x @@ -323,15 +333,15 @@ class Location(SQL_Base): self.z = z self.owner = owner - if len(args) > 0: - self.direction = TunnelDirection.str_to_tunnel_dir(args[0]) - self.tunnelNumber = int(args[1]) - self.tunnel_side = TunnelSide.str_to_tunnel_side(args[2]) + if tunnel is not None: + tunnel_info_list = tunnel.split(',') + self.tunnel_direction = TunnelDirection.str_to_tunnel_dir(tunnel_info_list[0]) + self.tunnel_number = int(tunnel_info_list[1]) + self.tunnel_side = TunnelSide.str_to_tunnel_side(tunnel_info_list[2]) - if len(args) > 3: - self.dimension = Dimension.str_to_dimension(args[3]) - - if self.dimension is None: + if self.dimension is not None: + self.dimension = self.dimension = Dimension.str_to_dimension(dimension) + else: self.dimension = Dimension.overworld except (ValueError, IndexError): @@ -341,10 +351,10 @@ class Location(SQL_Base): return '(x= {}, y= {}, z= {}) in the {}'.format(self.x, self.y, self.z, self.dimension.value.title()) def nether_tunnel_addr_to_str(self): - return '{} {} {}'.format(self.direction.value.title(), self.tunnelNumber, self.tunnel_side.value.title()) + return '{} {} {}'.format(self.tunnel_direction.value.title(), self.tunnel_number, self.tunnel_side.value.title()) def __str__(self): - if self.direction is not None: + if self.tunnel_direction is not None: return "Name: {}, Position: {}, Tunnel: {}".format(self.name, self.pos_to_str(), self.nether_tunnel_addr_to_str()) else: @@ -360,8 +370,8 @@ class Shop(Location): 'polymorphic_identity': 'Shop', } - def __init__(self, name, x, y, z, owner, args): - Location.__init__(self, name, x, y, z, owner, args) + def __init__(self, name, x, y, z, owner, tunnel, dimension): + Location.__init__(self, name, x, y, z, owner, tunnel, dimension) class ItemListing(SQL_Base): diff --git a/Geoffrey.py b/Geoffrey.py index 509f558..d2f8e00 100644 --- a/Geoffrey.py +++ b/Geoffrey.py @@ -2,6 +2,7 @@ from discord.ext import commands from DatabaseModels import * from BotErrors import * from MinecraftAccountInfoGrabber import * +from itertools import zip_longest import configparser import shlex #from WebInterface import * @@ -67,7 +68,7 @@ async def test(): @bot.command(pass_context=True) async def register(ctx): ''' - Registers your discord and minecraft account with the the database. You must do this before adding entries to + Registers your Discord and Minecraft account with the the database. You must do this before adding entries to the database. ''' @@ -85,16 +86,31 @@ async def register(ctx): async def addbase(ctx, name: str, x_pos: int, y_pos: int, z_pos: int, * args): ''' Add your base to the database. - The tunnel address is optional. - The default dimension is the overworld. Valid options: overworld, nether, end - ?addbase [Base Name] [X Coordinate] [Y Coordinate] [Z Coordinate] [Tunnel Color] - [Tunnel Position] [Side] [Dimension] + The tunnel address is optional. + The default dimension is the overworld. Valid options: overworld, nether, end + + ?addbase [Base Name] [X Coordinate] [Y Coordinate] [Z Coordinate] [Tunnel Color] [Optional Flags] + + Optional Flags: + -t [Tunnel Color],[Tunnel Position],[Side] + -d [dimension] ''' - player_name = get_nickname(ctx.message.author) + flags = get_args_dict(args) + tunnel = None + dimension = None + if len(flags) > 0: + if '-t' in flags: + tunnel = flags['-t'] + + if '-d' in flags: + dimension = flags['-d'] + + + await bot.say('Test ' + tunnel) try: - base = database_interface.add_location(ctx.message.author.id, name, x_pos, y_pos, z_pos, args) + shop = database_interface.add_shop(ctx.message.author.id, name, x_pos, y_pos, z_pos, tunnel, dimension) except LocationInitError: raise commands.UserInputError @@ -102,19 +118,32 @@ async def addbase(ctx, name: str, x_pos: int, y_pos: int, z_pos: int, * args): ' to the database.'.format(ctx.message.author.mention, base.name, base.pos_to_str())) @bot.command(pass_context=True) -async def addshop(ctx, name: str, x_pos: int, y_pos: int, z_pos: int, * args): +async def addshop(ctx, name: str, x_pos: int, y_pos: int, z_pos: int, *args): ''' Adds a shop to the database. - The tunnel address is optional. - The default dimension is the overworld. Valid options: overworld, nether, end - ?addbase [Base Name] [X Coordinate] [Y Coordinate] [Z Coordinate] [Tunnel Color] - [Tunnel Position] [Side] {Dimension] + The tunnel address is optional. + The default dimension is the overworld. Valid options: overworld, nether, end + + ?addbase [Shop Name] [X Coordinate] [Y Coordinate] [Z Coordinate] [Optional Flags] + + Optional Flags: + -t [Tunnel Color],[Tunnel Position],[Side] + -d [dimension] ''' - player_name = get_nickname(ctx.message.author) + flags = get_args_dict(args) + tunnel = None + dimension = None + + if len(flags) > 0: + if '-t' in flags: + tunnel = flags['-t'] + + if '-d' in flags: + dimension = flags['-d'] try: - shop = database_interface.add_shop(ctx.message.author.id, name, x_pos, y_pos, z_pos, args) + shop = database_interface.add_shop(ctx.message.author.id, name, x_pos, y_pos, z_pos, tunnel, dimension) except LocationInitError: raise commands.UserInputError @@ -157,19 +186,29 @@ async def delete(ctx, name: str): async def findaround(ctx, x_pos: int, z_pos: int, * args): ''' Finds all the locations around a certain point that are registered in the database - The Radius argument defaults to 200 blocks if no value is given - ?findbasearound [X Coordinate] [Z Coordinate] [Radius] + The radius defaults to 200 blocks if no value is given + Default dimension is overworld + + ?findaround [X Coordinate] [Z Coordinate] [Optional Flags] + + Optional Flags: + -r [radius] + -d [dimension] ''' radius = 200 + dimension = 'Overworld' - if len(args) > 0: - try: - radius = int(args[0]) - except ValueError: - raise commands.UserInputError + flags = get_args_dict(args) - base_list = database_interface.find_location_around(x_pos, z_pos, radius) + if len(flags) > 0: + if '-r' in flags: + radius = int(flags['-r']) + + if '-d' in flags: + dimension = flags['-d'] + + base_list = database_interface.find_location_around(x_pos, z_pos, radius, dimension) if len(base_list) != 0: base_string = loc_list_to_string(base_list, '{} \n{}') @@ -185,7 +224,8 @@ async def findaround(ctx, x_pos: int, z_pos: int, * args): async def additem(ctx, shop_name: str, item_name: str, amount: int, diamond_price: int): ''' Adds an item to a shop's inventory. Amount for diamond price. - ?additem [Shop name] [Item Name] [Amount] [Price] + + ?additem [Shop name] [Item Name] [Amount] [Price] ''' try: @@ -205,7 +245,8 @@ async def additem(ctx, shop_name: str, item_name: str, amount: int, diamond_pric async def selling(ctx, item_name: str): ''' Lists all the shops selling an item - ?selling [item] + + ?selling [item] ''' shop_list = database_interface.find_shop_selling_item(item_name) @@ -217,7 +258,8 @@ async def selling(ctx, item_name: str): async def shopinfo(ctx, shop_name: str): ''' Lists the information and inventory of a shop - ?shopinfo [Shop Name] + + ?shopinfo [Shop Name] ''' shop = database_interface.find_shop_by_name(shop_name)[0] inv_list = database_interface.get_shop_inventory(shop) @@ -251,6 +293,13 @@ def loc_list_to_string(loc_list, str_format='{}\n{}'): return loc_string +def get_args_dict(args): + if len(args) != 0: + return dict(zip_longest(*[iter(args)] * 2, fillvalue="")) + else: + return {} + + def create_config(): config['Discord'] = {'Token': ''} config['SQL'] = {'Dialect+Driver': 'test', 'username': '', 'password':'', 'host': '', 'port': '', 'database':''} diff --git a/test_geoffreyDatabase.py b/test_geoffreyDatabase.py index f654555..79cd0ac 100644 --- a/test_geoffreyDatabase.py +++ b/test_geoffreyDatabase.py @@ -7,7 +7,7 @@ class TestGeoffreyDatabase(TestCase): def setUp(self): self.interface = DiscordDatabaseInterface('sqlite:///:memory:') self.owner = Player('ZeroHD', '143072699567177728') - self.loc = Location('test', 1, 2, 3, self.owner, ['Green', 0, 'Right', 'Nether']) + self.loc = Location('test', 1, 2, 3, self.owner, 'Green,105,Right', 'Nether') def test_add_object(self): self.interface.database.add_object(self.loc) @@ -43,8 +43,8 @@ class TestGeoffreyDatabase(TestCase): self.assertRaises(DeleteEntryError, self.interface.database.delete_entry, Location, expr) def test_add_shop(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - shop = self.interface.add_shop('143072699567177728', 'test', 1, 2, 3, ['Green', 0, 'Right', 'Nether']) + owner = self.add_player() + shop = self.add_shop() self.assertEqual(type(shop), Shop) @@ -52,42 +52,51 @@ class TestGeoffreyDatabase(TestCase): self.assertEqual(shop_list[0].dimension, shop.dimension) self.assertEqual(shop_list[0].tunnel_side, shop.tunnel_side) + def add_shop(self): + return self.interface.add_shop('143072699567177728', 'test', 1, 2, 3, 'Green,105,Right', 'Nether') + + def add_player(self): + return self.interface.add_player('ZeroHD', '143072699567177728') + + def add_loc(self): + return self.interface.add_location('143072699567177728', 'test', 0, 0, 0, 'Green,105,Right', 'Nether') + def test_add_two_shops(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - shop1 = self.interface.add_shop('143072699567177728', 'test', 1, 2, 3, ['Green', 0, 'Right', 'Nether']) - shop2 = self.interface.add_shop('143072699567177728', 'no u', 1, 2, 3, ['Green', 0, 'Right', 'Nether']) + owner = self.add_player() + shop1 = self.add_shop() + shop2 = self.interface.add_shop('143072699567177728', 'no u', 1, 2, 3, 'Green,0,Right', 'Nether') loc_list = self.interface.find_location_by_owner_uuid('143072699567177728') self.assertEqual(loc_list[1].id, shop2.id) def test_add_item(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - self.interface.add_shop('143072699567177728', 'test', 1, 2, 3, ['Green', 0, "Right"]) + owner = self.add_player() + self.add_shop() self.interface.add_item('143072699567177728', 'test', 'dirt', 1, 15) shops = self.interface.find_shop_selling_item('dirt') self.assertEqual(shops[0].name, 'test') def test_find_location_by_owner(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - shop = self.interface.add_shop('143072699567177728', 'test', 1, 2, 3, ['Green', 0, "Right"]) + owner = self.add_player() + shop = self.add_shop() loc_list = self.interface.find_location_by_owner(owner) self.assertEqual(loc_list[0].id, shop.id) def test_find_location_by_name_and_owner(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - shop = self.interface.add_shop('143072699567177728', 'test', 1, 2, 3, ['Green', 0, "Right"]) + owner = self.add_player() + shop = self.add_shop() loc_list = self.interface.find_location_by_name_and_owner_uuid('143072699567177728', 'test') self.assertEqual(loc_list[0].id, shop.id) def test_delete_base(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - self.interface.add_location('143072699567177728', 'test', 1, 2, 3, ['Green', 0, "Right"]) + owner = self.add_player() + self.add_loc() self.interface.delete_location('143072699567177728', 'test') @@ -96,10 +105,10 @@ class TestGeoffreyDatabase(TestCase): self.assertEqual(len(loc_list), 0) def test_find_location_around(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - loc = self.interface.add_location('143072699567177728', 'test', 0, 0, 0, ['Green', 0, "Right"]) + owner = self.add_player() + loc = self.add_loc() - dim = Dimension.str_to_dimension("O") + dim = "o" loc_list = self.interface.find_location_around(100, 100, 100, dim) @@ -126,22 +135,22 @@ class TestGeoffreyDatabase(TestCase): self.assertEqual(loc_list[0].name, loc.name) def test_find_location_by_name(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - loc = self.interface.add_location('143072699567177728', 'test', 0, 0, 0, ['Green', 0, "Right"]) + owner = self.add_player() + loc = self.add_loc() loc_list = self.interface.find_location_by_name('test') self.assertEqual(loc_list[0].name, loc.name) def test_wrong_case(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') - loc = self.interface.add_location('143072699567177728', 'test', 0, 0, 0, ['Green', 0, "right"]) + owner = self.add_player() + loc = self.add_loc() loc_list = self.interface.find_location_by_owner_name('zerohd') self.assertEqual(loc_list[0].id, loc.id) - self.interface.add_shop('143072699567177728', 'testshop', 1, 2, 3, ['Green', 0, "lEft", "neThEr"]) + self.interface.add_shop('143072699567177728', 'testshop', 1, 2, 3, 'Green,0,lEft', 'neThEr') self.interface.add_item('143072699567177728', 'testshop', 'dirts', 1, 15) @@ -158,20 +167,20 @@ class TestGeoffreyDatabase(TestCase): self.assertEqual(loc_list[0].name, 'test') def test_big_input(self): - owner = self.interface.add_player('ZeroHD', '143072699567177728') + owner = self.add_player() loc = self.interface.add_location('143072699567177728', - 'TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT', 0, 0, 0, ['Green', 0, "Right"]) + 'TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT', 0, 0, 0) loc_list = self.interface.find_location_by_owner(owner) self.assertEqual(loc_list[0].id, loc.id) def test_duplicate_name(self): - self.interface.add_player('ZeroHD', '143072699567177728') - self.interface.add_location('143072699567177728', 'test', 0, 0, 0, ['Green', 0, "right"]) + self.add_player() + self.add_loc() self.assertRaises(LocationNameNotUniqueError, self.interface.add_location, - '143072699567177728', 'test', 0, 0, 0, ['Green', 0, "right"]) + '143072699567177728', 'test', 0, 0, 0)