diff --git a/CLI/BackupFactUpdater/__main__.py b/CLI/BackupFactUpdater/__main__.py index cd149d42..b89c0b57 100644 --- a/CLI/BackupFactUpdater/__main__.py +++ b/CLI/BackupFactUpdater/__main__.py @@ -3,7 +3,7 @@ Simple tool for automatically updating the backup fact file -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/CLI/DSSAUpdater/__main__.py b/CLI/DSSAUpdater/__main__.py index 2379f5ee..a24aa74f 100644 --- a/CLI/DSSAUpdater/__main__.py +++ b/CLI/DSSAUpdater/__main__.py @@ -6,7 +6,7 @@ Spreadsheet data used with permission from Fleetcomm and DSSA administration -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/CLI/DSSAUpdater/src/__init__.py b/CLI/DSSAUpdater/src/__init__.py index ad6714e9..8d45d7d9 100644 --- a/CLI/DSSAUpdater/src/__init__.py +++ b/CLI/DSSAUpdater/src/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for DSSA Updater module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/CLI/DSSAUpdater/src/carrier.py b/CLI/DSSAUpdater/src/carrier.py index f894b746..2d82fa3d 100644 --- a/CLI/DSSAUpdater/src/carrier.py +++ b/CLI/DSSAUpdater/src/carrier.py @@ -3,7 +3,7 @@ carrier.py - DSSA carrier object -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -79,7 +79,7 @@ def __eq__(self, other): """ if not isinstance(other, DSSACarrier): return False - return self.name.lower() == other.name.lower() + return self.name.casefold() == other.name.casefold() def __repr__(self): """Return a string representation of the carrier diff --git a/CLI/DSSAUpdater/src/scraper.py b/CLI/DSSAUpdater/src/scraper.py index 0166ee6f..1eaaf629 100644 --- a/CLI/DSSAUpdater/src/scraper.py +++ b/CLI/DSSAUpdater/src/scraper.py @@ -3,7 +3,7 @@ scraper.py - Spreadsheet scraper function, powered by BeautifulSoup -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -63,7 +63,7 @@ def scrape_spreadsheet(path: str, sheetlink: str, timestamp: str): for index, row in enumerate(rows): if row == [""] * 20: # This would be an empty row, ignore it continue - if row[2].lower() != "carrier operational": # 2 - Carrier status + if row[2].casefold() != "carrier operational": # 2 - Carrier status continue if row[9] == "": # 9 - Carrier name anomalies.append(f"{index + 1} - Name field has no value") diff --git a/CLI/EDDBFormatter/__main__.py b/CLI/EDDBFormatter/__main__.py index 7071856f..2bab338e 100644 --- a/CLI/EDDBFormatter/__main__.py +++ b/CLI/EDDBFormatter/__main__.py @@ -3,7 +3,7 @@ EDDB File Formatter -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 710fc690..082037d8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,11 +100,9 @@ have to start with a docstring of the following format: ```python """ -HalpyBOT v[VERSION] - file_name.py - One-sentence description of what this file does. -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/README.md b/README.md index 02ff2af4..5d87afb8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# HalpyBOT 1.6 +# HalpyBOT 1.6.1 This is the repository for HalpyBOT, the Hull Seals IRC Chatbot Assistant. # Description diff --git a/config/config_template.ini b/config/config_template.ini index c8495668..a211c639 100644 --- a/config/config_template.ini +++ b/config/config_template.ini @@ -84,4 +84,8 @@ message_channel = #seal-bob failure_button = False [UserAgent] -agent_comment = Your Organization Here \ No newline at end of file +agent_comment = Your Organization Here + +[YOURLS] +uri = YOURLS URL Here +pwd = Your PasswordlessAPI Key here diff --git a/data/help/commands.json b/data/help/commands.json index 75147599..8f3d0776 100644 --- a/data/help/commands.json +++ b/data/help/commands.json @@ -182,6 +182,11 @@ "aliases": [], "arguments": "[channel] [text]", "use": "Make the Bot say something" + }, + "shorten": { + "aliases": [], + "arguments": "[url]", + "use": "Shorten a provided URL" } }, "Delay": { diff --git a/halpybot/__init__.py b/halpybot/__init__.py index 36242f96..2a8ceb1d 100644 --- a/halpybot/__init__.py +++ b/halpybot/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for HalpyBOT core -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -12,7 +10,7 @@ from halpybot.packages.configmanager import config -__version__ = "1.5.3" +__version__ = "1.6.1" DEFAULT_USER_AGENT = ( "HalpyBOT/" diff --git a/halpybot/commands/__init__.py b/halpybot/commands/__init__.py index c40be3ee..03cc9cbc 100644 --- a/halpybot/commands/__init__.py +++ b/halpybot/commands/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for HalpyBOT Commands -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -25,6 +23,7 @@ from . import bot_help from . import caseutils from . import drill +from . import misc __all__ = [ "delayedboard", @@ -42,4 +41,5 @@ "bot_help", "caseutils", "drill", + "misc", ] diff --git a/halpybot/commands/bot_help.py b/halpybot/commands/bot_help.py index 487aff51..64220b53 100644 --- a/halpybot/commands/bot_help.py +++ b/halpybot/commands/bot_help.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - bot_help.py - get command list and command details when queried -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/commands/caseutils.py b/halpybot/commands/caseutils.py index 2dfa8cc8..7e131f89 100644 --- a/halpybot/commands/caseutils.py +++ b/halpybot/commands/caseutils.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - caseutils.py - Commands involving the management of Seal cases -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/commands/delayedboard.py b/halpybot/commands/delayedboard.py index fdf94d58..54813d1d 100644 --- a/halpybot/commands/delayedboard.py +++ b/halpybot/commands/delayedboard.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - delayedboard.py - Delayed Case Board commands -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -49,6 +47,7 @@ async def cmd_create_delayed_case(ctx: Context, args: List[str]): try: results = await DelayedCase.open(case_status, message, ctx.sender) except NoDatabaseConnection: + logger.exception("Offline Mode Set, No Database Connection") return await ctx.reply( "Cannot create case: running in OFFLINE MODE. " "Contact a cyberseal immediately!" @@ -93,6 +92,7 @@ async def cmd_reopen_delayed_case(ctx: Context, args: List[str]): try: results = await DelayedCase.reopen(case_id, casestat, ctx.sender) except NoDatabaseConnection: + logger.exception("Offline Mode Set, No Database Connection") return await ctx.reply( "Cannot reopen case: running in OFFLINE MODE. " "Contact a cyberseal immediately!" @@ -127,6 +127,7 @@ async def cmd_close_delayed_case(ctx: Context, args: List[str]): case_id, 3, ctx.sender ) # set casestat to 3 to close case except NoDatabaseConnection: + logger.exception("Offline Mode Set, No Database Connection") return await ctx.reply( "Cannot update case: running in OFFLINE MODE. " "Contact a cyberseal immediately!" @@ -163,6 +164,7 @@ async def cmd_update_delayed_status(ctx: Context, args: List[str]): try: results = await DelayedCase.status(case_id, casestat, ctx.sender) except NoDatabaseConnection: + logger.exception("Offline Mode Set, No Database Connection") return await ctx.reply( "Cannot update case: running in OFFLINE MODE. " "Contact a cyberseal immediately!" @@ -207,6 +209,7 @@ async def cmd_update_delayed_notes(ctx: Context, args: List[str]): try: results = await DelayedCase.notes(case_id, message, ctx.sender) except NoDatabaseConnection: + logger.exception("Offline Mode Set, No Database Connection") return await ctx.reply( "Cannot update case: running in OFFLINE MODE. " "Contact a cyberseal immediately!" @@ -235,6 +238,7 @@ async def cmd_check_delayed_cases(ctx: Context, args: List[str]): try: count = await DelayedCase.check() except NoDatabaseConnection: + logger.exception("Offline Mode Set, No Database Connection") return await ctx.reply( "Cannot connect to board: running in OFFLINE MODE. " "Contact a cyberseal immediately!" @@ -287,6 +291,7 @@ async def cmd_update_delayed_case(ctx: Context, args: List[str]): notesout = await DelayedCase.notes(case_id, message, ctx.sender) except NoDatabaseConnection: + logger.exception("Offline Mode Set, No Database Connection") return await ctx.reply( "Cannot update case: running in OFFLINE MODE. " "Contact a cyberseal immediately!" diff --git a/halpybot/commands/drill.py b/halpybot/commands/drill.py index f4b54bbf..7bc75bfd 100644 --- a/halpybot/commands/drill.py +++ b/halpybot/commands/drill.py @@ -1,16 +1,14 @@ """ -HalpyBOT v1.6 - drill.py - Commands for the training and drilling of Seals -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License See license.md """ from typing import List - +from loguru import logger from ..packages.command import Commands, get_help_text from ..packages.checks import Require, Drilled from ..packages.models import Context @@ -158,4 +156,5 @@ async def lookup(system): ) return "System Not Found in EDSM.\nPlease check system name with client " except EDSMLookupError: + logger.exception("Something went wrong with EDSM.") return "Unable to query EDSM" diff --git a/halpybot/commands/edsm.py b/halpybot/commands/edsm.py index 8a8fcd81..73a5145d 100644 --- a/halpybot/commands/edsm.py +++ b/halpybot/commands/edsm.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - edsm.py - EDSM Interface commands -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -24,6 +22,7 @@ diversions, NoResultsEDSM, NoNearbyEDSM, + EDSMReturnError, ) from ..packages.command import Commands, get_help_text from ..packages.models import Context @@ -60,7 +59,11 @@ async def cmd_systemlookup(ctx: Context, args: List[str]): return await ctx.reply( f"No system named {system} was found in the EDSM database." ) - + except EDSMReturnError: + logger.exception("Received malformed reply from EDSM.") + return await ctx.reply( + f"Received a reply from EDSM about {system}, but could not process the return." + ) except EDSMLookupError: logger.exception("Failed to query EDSM for system details.") return await ctx.reply("Failed to query EDSM for system details.") @@ -91,9 +94,13 @@ async def cmd_cmdrlocate(ctx: Context, args: List[str]): location = await Commander.location(name=cmdr, cache_override=cache_override) except NoResultsEDSM: return await ctx.reply(f"No CMDR named {cmdr} was found in the EDSM database.") + except EDSMReturnError: + logger.exception("Received malformed reply from EDSM.") + return await ctx.reply( + f"Received a reply from EDSM about {cmdr}, but could not process the return." + ) except EDSMConnectionError: logger.exception("Failed to query EDSM for commander data.") - # kill it. kill it with fire. ~ TheUnkn0wn1 return await ctx.reply("Failed to query EDSM for commander data.") if location is None: @@ -139,6 +146,11 @@ async def cmd_distlookup(ctx: Context, args: List[str]): return await ctx.reply( "No system and/or commander was found in the EDSM database for one of the points." ) + except EDSMReturnError: + logger.exception("Received a malformed reply from EDSM.") + return await ctx.reply( + "Received a reply from EDSM, but could not process the return." + ) except EDSMLookupError: logger.exception("Failed to query EDSM for system or CMDR details.") return await ctx.reply("Failed to query EDSM for system or CMDR details.") @@ -191,8 +203,11 @@ async def cmd_landmarklookup(ctx: Context, args: List[str]): f"The closest DSSA Carrier is in {dssa}, {distance} LY " f"{direction} of {system}." ) - logger.exception("Failed to query EDSM for landmark details.") - return await ctx.reply("Failed to query EDSM for landmark details.") + except EDSMReturnError: + logger.exception("Received a malformed reply from EDSM.") + return await ctx.reply( + f"Received a reply from EDSM about {system}, but could not process the return." + ) @Commands.command("dssa") @@ -231,6 +246,11 @@ async def cmd_dssalookup(ctx: Context, args: List[str]): return await ctx.reply( f"No system and/or commander named {system} was found in the EDSM database." ) + except EDSMReturnError: + logger.exception("Received a malformed reply from EDSM.") + return await ctx.reply( + f"Received a reply from EDSM about {system}, but could not process the return." + ) except EDSMLookupError: logger.exception("Failed to query EDSM for DSSA details.") return await ctx.reply("Failed to query EDSM for DSSA details.") @@ -264,6 +284,11 @@ async def cmd_coordslookup(ctx, args: List[str]): return await ctx.reply( f"No system and/or commander named {system} was found in the EDSM database." ) + except EDSMReturnError: + logger.exception("Received a malformed reply from EDSM.") + return await ctx.reply( + f"Received a reply from EDSM about {system}, but could not process the return." + ) except EDSMLookupError: logger.exception("Failed to query EDSM for coordinate details.") return await ctx.reply("Failed to query EDSM for coordinate details.") @@ -318,6 +343,11 @@ async def cmd_diversionlookup(ctx: Context, args: List[str]): return await ctx.reply( f"No system and/or commander named {system} was found in the EDSM database." ) + except EDSMReturnError: + logger.exception("Received a malformed reply from EDSM.") + return await ctx.reply( + f"Received a reply from EDSM about {system}, but could not process the return." + ) except EDSMLookupError: logger.exception("Failed to query EDSM for coordinate details.") return await ctx.reply("Failed to query EDSM for coordinate details.") diff --git a/halpybot/commands/fact.py b/halpybot/commands/fact.py index a91a30aa..1860b49a 100644 --- a/halpybot/commands/fact.py +++ b/halpybot/commands/fact.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - fact.py - Fact module management commands -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -42,7 +40,7 @@ async def cmd_getfactdata(ctx: Context, args: List[str]): """ if not args or len(args) != 1: return await ctx.reply(get_help_text("factinfo")) - name = args[0].split("-")[0] + name = args[0].split("-")[0].casefold() lang = args[0].split("-")[1] if len(args[0].split("-")) == 2 else "en" fact: Optional[Fact] = await Facts.get(name, lang) if fact is None: @@ -50,8 +48,8 @@ async def cmd_getfactdata(ctx: Context, args: List[str]): langlist = await Facts.lang_by_fact(name) reply = ( f"Fact: {fact.name}\n" - f"Language: {langcodes[lang.lower()]} ({fact.language})\n" - f"All langs: {', '.join(f'{langcodes[lan.lower()]} ({lan.upper()})' for lan in langlist)}\n" + f"Language: {langcodes[lang.casefold()]} ({fact.language})\n" + f"All langs: {', '.join(f'{langcodes[lan.casefold()]} ({lan.upper()})' for lan in langlist)}\n" f"ID: {fact.ID}\n" f"Author: {fact.author}\n" f"Text: {fact.raw_text}" @@ -72,7 +70,7 @@ async def cmd_addfact(ctx: Context, args: List[str]): if not args or len(args) < 2: return await ctx.reply(get_help_text("addfact")) lang = args[0].split("-")[1] if len(args[0].split("-")) == 2 else "en" - name = args[0].split("-")[0] + name = args[0].split("-")[0].casefold() if lang not in langcodes: return await ctx.reply( @@ -88,6 +86,7 @@ async def cmd_addfact(ctx: Context, args: List[str]): return await ctx.reply("Fact has been added.") except NoDatabaseConnection: + logger.exception("Offline Mode Set, No Database Connection") return await ctx.reply( "Unable to add fact: No database connection available. Entering Offline Mode, " "contact a cyberseal." @@ -114,7 +113,7 @@ async def cmd_deletefact(ctx: Context, args: List[str]): if not args or len(args) != 1: return await ctx.reply(get_help_text("deletefact")) - name = args[0].split("-")[0] + name = args[0].split("-")[0].casefold() lang = args[0].split("-")[1] if len(args[0].split("-")) == 2 else "en" if await Facts.get(name, lang) is None: @@ -149,7 +148,7 @@ async def cmd_listfacts(ctx: Context, args: List[str]): if not args: lang = "en" else: - lang = args[0].lower() + lang = args[0].casefold() # Input validation if lang not in langcodes: @@ -160,9 +159,9 @@ async def cmd_listfacts(ctx: Context, args: List[str]): factlist = Facts.list(lang) if len(factlist) == 0: - return await ctx.redirect(f"No {langcodes[lang.lower()]} facts found.") + return await ctx.redirect(f"No {langcodes[lang.casefold()]} facts found.") return await ctx.redirect( - f"All {langcodes[lang.lower()]} facts:\n" + f"All {langcodes[lang.casefold()]} facts:\n" f"{', '.join(fact for fact in factlist)}" ) @@ -177,9 +176,9 @@ async def cmd_editfact(ctx: Context, args: List[str]): Aliases: updatefact """ if not args or len(args) < 2: - return await ctx.reply(get_help_text("deletefact")) + return await ctx.reply(get_help_text("editfact")) - name = args[0].split("-")[0] + name = args[0].split("-")[0].casefold() lang = args[0].split("-")[1] if len(args[0].split("-")) == 2 else "en" fact = await Facts.get(name, lang) diff --git a/halpybot/commands/forcejoin.py b/halpybot/commands/forcejoin.py index 400e91eb..fde3ead7 100644 --- a/halpybot/commands/forcejoin.py +++ b/halpybot/commands/forcejoin.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - forcejoin.py - SAJOIN command module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -33,7 +31,7 @@ async def cmd_sajoin(ctx: Context, args: List[str]): return await ctx.reply(get_help_text("forcejoin")) # Convert channel name to lower case to avoid issues with the already-in-channel check - args[1] = args[1].lower() + args[1] = args[1].casefold() botuser = await User.get_info(ctx.bot, ctx.bot.nickname) @@ -61,7 +59,7 @@ async def cmd_sajoin(ctx: Context, args: List[str]): # Now we manually confirm that the SAJOIN was successful channels = await User.get_channels(ctx.bot, args[0]) - if args[1].lower() in channels: + if args[1].casefold() in channels: return await ctx.reply(f"{str(args[0])} forced to join {str(args[1])}") return await ctx.reply("Oh noes! something went wrong, contact a cyberseal!") diff --git a/halpybot/commands/manual_case.py b/halpybot/commands/manual_case.py index b67c6cad..8da94be2 100644 --- a/halpybot/commands/manual_case.py +++ b/halpybot/commands/manual_case.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - manual_case.py - Manual case creation module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -86,6 +84,7 @@ async def cmd_manual_case(ctx: Context, args: List[str]): hook_id=webhook_id, hook_token=webhook_token, body=cn_message ) except WebhookSendError: + logger.exception("Webhook could not be sent.") await ctx.reply( "WARNING: Unable to send notification to Discord. Contact a cyberseal!" ) @@ -132,6 +131,7 @@ async def cmd_tsping(ctx: Context, args: List[str]): hook_id=webhook_id, hook_token=webhook_token, body=cn_message ) except WebhookSendError: + logger.exception("Webhook could not be sent.") await ctx.reply( "WARNING: Unable to send notification to Discord. Contact a cyberseal!" ) diff --git a/halpybot/commands/misc.py b/halpybot/commands/misc.py new file mode 100644 index 00000000..29bfde55 --- /dev/null +++ b/halpybot/commands/misc.py @@ -0,0 +1,32 @@ +""" +misc.py - Miscellaneous Bot Functionality + +Copyright (c) The Hull Seals, +All rights reserved. + +Licensed under the GNU General Public License +See license.md +""" +from typing import List +from loguru import logger +from ..packages.utils import shorten +from ..packages.checks import Require, Drilled +from ..packages.command import Commands, get_help_text +from ..packages.models import Context + + +@Commands.command("shorten") +@Require.permission(Drilled) +async def cmd_shorten(ctx: Context, args: List[str]): + """ + Shorten a given URL with the configured YOURLS API + + Usage: !shorten [url] + Aliases: n/a + """ + if not args: + return await ctx.reply(get_help_text("shorten")) + logger.info(f"{ctx.sender} requested shortening of {args[0]}") + surl = await shorten(args[0]) + return await ctx.reply(f"{ctx.sender}: Here's your short URL: {surl}.") + diff --git a/halpybot/commands/notify.py b/halpybot/commands/notify.py index 0e6e841d..47343521 100644 --- a/halpybot/commands/notify.py +++ b/halpybot/commands/notify.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - notify.py - AWS SNS Interface -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -67,7 +65,7 @@ async def cmd_listnotify(ctx: Context, args: List[str]): if len(args) == 0: return await ctx.reply(get_help_text("notifyinfo details")) - group = args[0].lower().strip() + group = args[0].casefold().strip() if group in ["staff", "moderators", "hull-seals-staff"]: group = "staff" @@ -105,7 +103,7 @@ async def cmd_subscribe(ctx: Context, args: List[str]): if len(args) <= 1: return await ctx.reply(get_help_text("addsub")) - group = args[0].lower().strip() + group = args[0].casefold().strip() if group in ["staff", "moderators", "hull-seals-staff"]: group = "staff" diff --git a/halpybot/commands/ping.py b/halpybot/commands/ping.py index 924f714a..b11d415b 100644 --- a/halpybot/commands/ping.py +++ b/halpybot/commands/ping.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - ping.py - Ping the bot, database, and external services -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -97,10 +95,9 @@ async def cmd_serverstat(ctx: Context, args: List[str]): "Unable to verify Elite Status, having issues connecting to the Elite API." ) from e if len(responses) == 0: - await ctx.reply("ERROR! Elite returned an empty reply.") - else: - message = responses["text"] - code = responses["status"] - await ctx.reply( - f"The Elite servers are {message} (Status Code {code}) according to Frontier." - ) + return await ctx.reply("ERROR! Elite returned an empty reply.") + message = responses["text"] + code = responses["status"] + await ctx.reply( + f"The Elite servers are {message} (Status Code {code}) according to Frontier." + ) diff --git a/halpybot/commands/puppet.py b/halpybot/commands/puppet.py index 1d5d0fe0..0ff2ea3e 100644 --- a/halpybot/commands/puppet.py +++ b/halpybot/commands/puppet.py @@ -1,9 +1,7 @@ """" -HalpyBOT v1.6 - puppet.py - Bot sock puppet -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/commands/settings.py b/halpybot/commands/settings.py index 2d1f74f5..21cf05b2 100644 --- a/halpybot/commands/settings.py +++ b/halpybot/commands/settings.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - settings.py - bot settings commands -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -87,9 +85,9 @@ async def cmd_offline(ctx: Context, args: List[str]): f"{get_help_text('settings offline')}\nCurrent " f"offline setting: {config['Offline Mode']['enabled']}" ) - if args[0].lower() == "true" and config["Offline Mode"]["enabled"] != "True": + if args[0].casefold() == "true" and config["Offline Mode"]["enabled"] != "True": set_to = "True" - elif args[0].lower() == "false" and config["Offline Mode"]["enabled"] != "False": + elif args[0].casefold() == "false" and config["Offline Mode"]["enabled"] != "False": set_to = "False" config_write("Offline Mode", "warning override", "False") else: @@ -123,7 +121,7 @@ async def cmd_override_omw(ctx: Context, args: List[str]): f"{get_help_text('settings warning_override')}\n" f"Current warning override setting: {config['Offline Mode']['warning override']}" ) - request = args[0].lower() + request = args[0].casefold() if request in ("enable", "true"): config_write("Offline Mode", "warning override", "True") diff --git a/halpybot/commands/shutdown.py b/halpybot/commands/shutdown.py index 1889b718..39922c51 100644 --- a/halpybot/commands/shutdown.py +++ b/halpybot/commands/shutdown.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - shutdown.py - Will be with you shortly, please hold! -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/commands/time.py b/halpybot/commands/time.py index 18172f65..35c57ab8 100644 --- a/halpybot/commands/time.py +++ b/halpybot/commands/time.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - time.py - get in-game time -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/commands/userinfo.py b/halpybot/commands/userinfo.py index 52a5f93c..c55e6ef3 100644 --- a/halpybot/commands/userinfo.py +++ b/halpybot/commands/userinfo.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - userinfo.py - Seal whox lookup commands -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -32,7 +30,7 @@ async def cmd_whois(ctx: Context, args: List[str]): if len(args) == 0: return await ctx.redirect(get_help_text("whois")) cmdr = args[0] - if cmdr.lower() == "halpybot": + if cmdr.casefold() == "halpybot": return await ctx.redirect( "That's me! CMDR HalpyBOT has a Seal ID of 0, registered 14.8 billion years ago, " "is a DW2 Veteran and Founder Seal with registered CMDRs of Arf! Arf! Arf!, " diff --git a/halpybot/packages/announcer/__init__.py b/halpybot/packages/announcer/__init__.py index 3b9e0610..bdf9c3f2 100644 --- a/halpybot/packages/announcer/__init__.py +++ b/halpybot/packages/announcer/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Announcer module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/announcer/announcer.py b/halpybot/packages/announcer/announcer.py index 6601a938..02f78267 100644 --- a/halpybot/packages/announcer/announcer.py +++ b/halpybot/packages/announcer/announcer.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - announcer.py - Client announcement handler -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -15,6 +13,7 @@ import json from typing import List, Dict, Optional import pydle +from loguru import logger from ..edsm import ( checklandmarks, @@ -100,6 +99,7 @@ async def announce(self, announcement: str, args: Dict): try: await TwitterCasesAcc.tweet_case(ann, args) except TwitterConnectionError: + logger.exception("Unable to send case details to Twitter.") return except Exception as announcement_exception: raise AnnouncementError(Exception) from announcement_exception diff --git a/halpybot/packages/announcer/dc_webhook.py b/halpybot/packages/announcer/dc_webhook.py index c17cd705..5efa34ec 100644 --- a/halpybot/packages/announcer/dc_webhook.py +++ b/halpybot/packages/announcer/dc_webhook.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - dc_webhook.py - Discord webhook support -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/announcer/twitter.py b/halpybot/packages/announcer/twitter.py index 7c792764..5e7217e2 100644 --- a/halpybot/packages/announcer/twitter.py +++ b/halpybot/packages/announcer/twitter.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - twitter.py - Send case announcements over -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/checks/__init__.py b/halpybot/packages/checks/__init__.py index 1e3e4e85..647cc8d0 100644 --- a/halpybot/packages/checks/__init__.py +++ b/halpybot/packages/checks/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Permission Checks module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/checks/checks.py b/halpybot/packages/checks/checks.py index 8075aaa6..22e5d957 100644 --- a/halpybot/packages/checks/checks.py +++ b/halpybot/packages/checks/checks.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - checks.py - Check check check... -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/command/__init__.py b/halpybot/packages/command/__init__.py index 3b16dcdb..95f6d58d 100644 --- a/halpybot/packages/command/__init__.py +++ b/halpybot/packages/command/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Commands module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/command/commandhandler.py b/halpybot/packages/command/commandhandler.py index ebd295f9..aee45e56 100644 --- a/halpybot/packages/command/commandhandler.py +++ b/halpybot/packages/command/commandhandler.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - commandhandler.py - Handle bot commands and facts -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -94,7 +92,7 @@ def get_group(cls, name: str): """ for group in cls._grouplist: - if name.lower() == group.name: + if name.casefold() == group.name: return group return None @@ -126,7 +124,7 @@ async def invoke_from_message( if message.startswith(config["IRC"]["commandPrefix"]): # Start off with assigning all variables we need parts = message[1:].split(" ") - command = parts[0].lower() + command = parts[0].casefold() args = parts[1:] args = [x for x in args if x] in_channel = bot.is_channel(channel) @@ -254,7 +252,7 @@ async def invoke_command( in the command execution itself. """ - command = command.lower() + command = command.casefold() # Sanity check if command not in self.command_list: raise CommandHandlerError("(sub)command not found.") @@ -305,10 +303,10 @@ def get_help_text(search_command: str): Returns: (str or None): Help instructions for a given command, None if unsuccessful. """ - search_command = search_command.lower() + search_command = search_command.casefold() for command_dict in json_dict.values(): for command, details in command_dict.items(): - command = command.lower() + command = command.casefold() if command == search_command or search_command in details["aliases"]: arguments = details["arguments"] aliases = details["aliases"] diff --git a/halpybot/packages/configmanager/__init__.py b/halpybot/packages/configmanager/__init__.py index f01c54e0..38470764 100644 --- a/halpybot/packages/configmanager/__init__.py +++ b/halpybot/packages/configmanager/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Config Manager module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/configmanager/config.py b/halpybot/packages/configmanager/config.py index 1fbfd8ce..5028c50c 100644 --- a/halpybot/packages/configmanager/config.py +++ b/halpybot/packages/configmanager/config.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - config.py - Configuration manager -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -45,4 +43,5 @@ def config_write(module: str, key: str, value): with open("config/config.ini", "w", encoding="UTF-8") as conf: config.write(conf) except (FileNotFoundError, PermissionError) as ex: + logger.exception("Error writing value to config file. Check permissions?") raise ConfigException from ex diff --git a/halpybot/packages/database/__init__.py b/halpybot/packages/database/__init__.py index d04f018d..a66f6cd0 100644 --- a/halpybot/packages/database/__init__.py +++ b/halpybot/packages/database/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Database Connection module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/database/connection.py b/halpybot/packages/database/connection.py index a8e3e09d..5c21a1bd 100644 --- a/halpybot/packages/database/connection.py +++ b/halpybot/packages/database/connection.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - connection.py - Database connection initialization script -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -12,7 +10,6 @@ import time import mysql.connector -from mysql.connector import MySQLConnection from loguru import logger from ..configmanager import config_write, config @@ -37,7 +34,7 @@ class NoDatabaseConnection(ConnectionError): """ -class DatabaseConnection(MySQLConnection): +class DatabaseConnection(mysql.connector.MySQLConnection): """Governing the connection to the database""" def __init__(self, autocommit: bool = True): diff --git a/halpybot/packages/delayedboard/__init__.py b/halpybot/packages/delayedboard/__init__.py index 9545a807..be92df06 100644 --- a/halpybot/packages/delayedboard/__init__.py +++ b/halpybot/packages/delayedboard/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Delayed Case Management module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/delayedboard/delayedboard.py b/halpybot/packages/delayedboard/delayedboard.py index 548cbfaf..7491aa86 100644 --- a/halpybot/packages/delayedboard/delayedboard.py +++ b/halpybot/packages/delayedboard/delayedboard.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - delayedboard.py - Database interaction for Delayed Board commands -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/edsm/__init__.py b/halpybot/packages/edsm/__init__.py index d618ae18..5ee47068 100644 --- a/halpybot/packages/edsm/__init__.py +++ b/halpybot/packages/edsm/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for Elite: Dangerous Star Map API interface module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -25,6 +23,7 @@ calc_direction, diversions, NoNearbyEDSM, + EDSMReturnError, ) __all__ = [ @@ -42,4 +41,5 @@ "calc_direction", "diversions", "NoNearbyEDSM", + "EDSMReturnError", ] diff --git a/halpybot/packages/edsm/edsm.py b/halpybot/packages/edsm/edsm.py index f71109bc..5cc66e7c 100644 --- a/halpybot/packages/edsm/edsm.py +++ b/halpybot/packages/edsm/edsm.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - edsm.py - Elite: Dangerous Star Map API interface module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -20,7 +18,7 @@ from pathlib import Path import json from time import time -from typing import Optional, Union, List +from cattrs.errors import ClassValidationError from loguru import logger import aiohttp import numpy as np @@ -57,9 +55,15 @@ class NoNearbyEDSM(EDSMLookupError): """ +class EDSMReturnError(EDSMLookupError): + """ + EDSM returned a reply, however the reply did not include key data needed to continue. + """ + + @dataclass class EDSMQuery: - object: Union[GalaxySystem, Commander, None] + object: typing.Union[GalaxySystem, Commander, None] time: time() @@ -99,7 +103,7 @@ def from_api(cls, api: edsm_classes.Galaxy) -> GalaxySystem: @classmethod async def get_info( cls, name, cache_override: bool = False - ) -> Optional[GalaxySystem]: + ) -> typing.Optional[GalaxySystem]: """Get a system object from the EDSM API. If the same object was requested less than @@ -152,7 +156,11 @@ async def get_info( # Return None if system doesn't exist if len(responses) == 0: return None - api: edsm_classes.Galaxy = cattr.structure(responses, edsm_classes.Galaxy) + try: + api: edsm_classes.Galaxy = cattr.structure(responses, edsm_classes.Galaxy) + except ClassValidationError as exc: + logger.exception("Error validating class. Invalid attributes.") + raise EDSMReturnError from exc # Store in cache and return sysobj = GalaxySystem.from_api(api=api) @@ -249,7 +257,7 @@ class Commander: name: str system: str coordinates: Coordinates - date: Optional[str] + date: typing.Optional[str] _lookupCache = {} @@ -264,7 +272,9 @@ def from_api(cls, name: str, api: edsm_classes.Commander) -> Commander: ) @classmethod - async def get_cmdr(cls, name, cache_override: bool = False) -> Optional[Commander]: + async def get_cmdr( + cls, name, cache_override: bool = False + ) -> typing.Optional[Commander]: """Get info about a CMDR from EDSM If the same object was requested less than @@ -313,7 +323,13 @@ async def get_cmdr(cls, name, cache_override: bool = False) -> Optional[Commande return None if responses["msgnum"] == 201: raise EDSMConnectionError - api: edsm_classes.Commander = cattr.structure(responses, edsm_classes.Commander) + try: + api: edsm_classes.Commander = cattr.structure( + responses, edsm_classes.Commander + ) + except ClassValidationError as exc: + logger.exception("Error validating class. Invalid attributes.") + raise EDSMReturnError from exc if api.system is None: raise EDSMConnectionError("Error! CMDR Exists, but unable to get info.") @@ -325,7 +341,9 @@ async def get_cmdr(cls, name, cache_override: bool = False) -> Optional[Commande return cmdrobj @classmethod - async def location(cls, name, cache_override: bool = False) -> Optional[Location]: + async def location( + cls, name, cache_override: bool = False + ) -> typing.Optional[Location]: """Get a CMDRs location Get a Location object for an EDSM commander. @@ -365,9 +383,9 @@ async def location(cls, name, cache_override: bool = False) -> Optional[Location class Edsm: def __init__(self): - self._carriers: Optional[typing.List[GalaxySystem]] = None - self._landmarks: Optional[typing.List[GalaxySystem]] = None - self._diversions: Optional[typing.List[EDDBSystem]] = None + self._carriers: typing.Optional[typing.List[GalaxySystem]] = None + self._landmarks: typing.Optional[typing.List[GalaxySystem]] = None + self._diversions: typing.Optional[typing.List[EDDBSystem]] = None @property def landmarks(self): @@ -585,7 +603,7 @@ class Diversion: item: float = field(converter=float) -Diversions = List[Diversion] +Diversions = typing.List[Diversion] async def diversions(edsm_sys_name, cache_override: bool = False) -> Diversions: diff --git a/halpybot/packages/facts/__init__.py b/halpybot/packages/facts/__init__.py index 92b730b1..c7de142d 100644 --- a/halpybot/packages/facts/__init__.py +++ b/halpybot/packages/facts/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Fact Manager module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/facts/facthandler.py b/halpybot/packages/facts/facthandler.py index 77b69f3d..88641c48 100644 --- a/halpybot/packages/facts/facthandler.py +++ b/halpybot/packages/facts/facthandler.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - facthandler.py - Database interaction for the fact module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -14,7 +12,7 @@ from typing import List, Optional import json import re - +from loguru import logger from ..database import DatabaseConnection, NoDatabaseConnection from ..configmanager import config from ..command import Commands @@ -153,7 +151,7 @@ def _write(self): cursor = database_connection.cursor() args = ( self._name, - self._lang.lower(), + self._lang.casefold(), self._raw_text, self._author, self._ID, @@ -166,6 +164,7 @@ def _write(self): args, ) except NoDatabaseConnection: + logger.exception("No database connection. Unable to update fact.") raise FactUpdateError( "Fact was probably updated locally but could " "not be uploaded to the database." @@ -214,6 +213,7 @@ async def fetch_facts(self, preserve_current: bool = False): try: await self._from_database() except NoDatabaseConnection: + logger.exception("No database connection. Unable to retreive facts.") if not preserve_current: await self._from_local() raise @@ -276,7 +276,7 @@ async def add_fact(self, name: str, lang: str, text: str, author: str): if name in Commands.command_list: raise InvalidFactException("This fact is already an existing command") # Check if we have an English fact: - if not await self.get(name) and lang.lower() != "en": + if not await self.get(name) and lang.casefold() != "en": raise InvalidFactException( "All registered facts must have an English version" ) @@ -343,7 +343,7 @@ async def delete_fact(self, name: str, lang: str = "en"): NoDatabaseConnection: Raised when entering offline mode """ - if lang.lower() == "en" and len(await self.lang_by_fact(name)) > 1: + if lang.casefold() == "en" and len(await self.lang_by_fact(name)) > 1: raise FactHandlerError( "Cannot delete English fact if other languages " "are registered for that fact name." @@ -372,7 +372,7 @@ def list(self, lang: Optional[str] = None) -> List[tuple]: return list(self._fact_cache.keys()) langlist = [] for fact in self._fact_cache: - if fact[1].lower() == lang.lower(): + if fact[1].casefold() == lang.casefold(): langlist.append(fact[0]) return langlist diff --git a/halpybot/packages/ircclient/__init__.py b/halpybot/packages/ircclient/__init__.py index bb38a183..6c389763 100644 --- a/halpybot/packages/ircclient/__init__.py +++ b/halpybot/packages/ircclient/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the IRC Client module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/ircclient/_listsupport.py b/halpybot/packages/ircclient/_listsupport.py index 3047dfd2..4c7548cb 100644 --- a/halpybot/packages/ircclient/_listsupport.py +++ b/halpybot/packages/ircclient/_listsupport.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - listsupport.py - Handler for LIST IRC commands -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/ircclient/halpybot.py b/halpybot/packages/ircclient/halpybot.py index da48be8d..08bb5b30 100644 --- a/halpybot/packages/ircclient/halpybot.py +++ b/halpybot/packages/ircclient/halpybot.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - halpybot.py - Pydle client module for HalpyBOT -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -54,6 +52,7 @@ async def handle_forever(self): try: await super().handle_forever() except ConnectionResetError as cre: + logger.exception("HalpyBOT has crashed with a CRE.") await crash_notif("Connection Reset Error", cre) # Sometimes, the bot will fail in its attempts to reconnect with a CAE @@ -61,6 +60,7 @@ async def _disconnect(self, expected): try: await super()._disconnect(expected) except ConnectionAbortedError as cae: + logger.exception("HalpyBOT has crashed with a CAE.") await crash_notif("Connection Aborted Error", cae) # Handle the clean disconnect but fail to reconnect of the bot diff --git a/halpybot/packages/models/__init__.py b/halpybot/packages/models/__init__.py index fe146efa..abc90b50 100644 --- a/halpybot/packages/models/__init__.py +++ b/halpybot/packages/models/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the bot models -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/models/context.py b/halpybot/packages/models/context.py index 8aea6761..b0eb5bc4 100644 --- a/halpybot/packages/models/context.py +++ b/halpybot/packages/models/context.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - context.py - Message context object -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/models/edsm_classes.py b/halpybot/packages/models/edsm_classes.py index aa866b73..a8451000 100644 --- a/halpybot/packages/models/edsm_classes.py +++ b/halpybot/packages/models/edsm_classes.py @@ -1,6 +1,4 @@ """ -HalpyBOT v1.6 - edsm_classes.py - (data)classes for the EDSM module This module houses the API datamodel, which EXACTLY matches what EDSM @@ -8,7 +6,7 @@ It is STRONGLY recommended to not use these datatypes directly in internal code, but to have an adapter in the middle. -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/models/user.py b/halpybot/packages/models/user.py index b6ad48b6..5e707fc3 100644 --- a/halpybot/packages/models/user.py +++ b/halpybot/packages/models/user.py @@ -10,11 +10,9 @@ Copyright (c) 2018, The Fuel Rats Mischief All rights reserved. -HalpyBOT v1.6 - user.py - User dataclass -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -90,7 +88,7 @@ def process_vhost(cls, vhost: Optional[str]) -> Optional[str]: if vhost is None: return None # RixxanCheck(TM) - if vhost.lower().endswith("rixxan.admin.hullseals.space"): + if vhost.casefold().endswith("rixxan.admin.hullseals.space"): return "rixxan.admin.hullseals.space" # sanity / security check if not vhost.endswith(".hullseals.space"): @@ -117,5 +115,5 @@ async def get_channels(cls, bot: pydle.Client, nick: str) -> Optional[list]: user = await bot.whois(nick) channels = user["channels"] return [ - ch.translate({ord(c): None for c in "+%@&~"}).lower() for ch in channels + ch.translate({ord(c): None for c in "+%@&~"}).casefold() for ch in channels ] diff --git a/halpybot/packages/notify/__init__.py b/halpybot/packages/notify/__init__.py index 3d8ffc8f..5b0dd0ca 100644 --- a/halpybot/packages/notify/__init__.py +++ b/halpybot/packages/notify/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for boto3 notification module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/notify/notify.py b/halpybot/packages/notify/notify.py index 00a38eb7..43717cae 100644 --- a/halpybot/packages/notify/notify.py +++ b/halpybot/packages/notify/notify.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - notify.py - Amazon Web Services Simple Notification Service interface -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/seals/__init__.py b/halpybot/packages/seals/__init__.py index 13ccf824..64492eae 100644 --- a/halpybot/packages/seals/__init__.py +++ b/halpybot/packages/seals/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Seal WHOIS module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/seals/userinfo.py b/halpybot/packages/seals/userinfo.py index bc2b7184..bca293e7 100644 --- a/halpybot/packages/seals/userinfo.py +++ b/halpybot/packages/seals/userinfo.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - userinfo.py - Fetching information about a registered user. -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/packages/utils/__init__.py b/halpybot/packages/utils/__init__.py index 781ebb9f..96928ec3 100644 --- a/halpybot/packages/utils/__init__.py +++ b/halpybot/packages/utils/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Seal user info module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -11,5 +9,6 @@ """ from .utils import get_time_seconds, strip_non_ascii, language_codes +from .shorten import shorten -__all__ = ["strip_non_ascii", "get_time_seconds", "language_codes"] +__all__ = ["strip_non_ascii", "get_time_seconds", "language_codes", "shorten"] diff --git a/halpybot/packages/utils/shorten.py b/halpybot/packages/utils/shorten.py new file mode 100644 index 00000000..09d05caa --- /dev/null +++ b/halpybot/packages/utils/shorten.py @@ -0,0 +1,73 @@ +""" +shorten.py - YOURLS URL shortener + +Copyright (c) The Hull Seals, +All rights reserved. + +Licensed under the GNU General Public License +See license.md +""" +import aiohttp +from loguru import logger +from halpybot import DEFAULT_USER_AGENT +from ..configmanager import config + + +class YOURLSError(Exception): + """ + Base class for YOURLS link errors + """ + + +class YOURLSNoResponse(YOURLSError): + """ + An exception occurred while sending data to or receiving from a YOURLS API + """ + + +class YOURLSBadResponse(YOURLSError): + """ + YOURLS returned an unprocessable response. + """ + + +async def shorten(url): + """ + Shorten a given URL via a YOURLS passwordless API call + + Args: + url (str): The URL to shorten + + Returns: + surl (str): The shortened URL + + Raises: + YOURLSNoResponse: YOURLS did not respond by the timeout. + """ + if not url.lower().startswith("http"): + url = "https://" + url + + try: + async with aiohttp.ClientSession( + headers={"User-Agent": DEFAULT_USER_AGENT} + ) as session: + async with await session.get( + f"{config['YOURLS']['uri']}/yourls-api.php", + params={ + "signature": config["YOURLS"]["pwd"], + "action": "shorturl", + "format": "json", + "url": url, + }, + timeout=10, + ) as response: + responses = await response.json() + except aiohttp.ClientError as ex: + logger.exception("YOURLS Did Not Respond") + raise YOURLSNoResponse from ex + + if not responses: + raise YOURLSNoResponse + if "shorturl" not in responses: + raise YOURLSBadResponse + return responses["shorturl"] diff --git a/halpybot/packages/utils/utils.py b/halpybot/packages/utils/utils.py index b3627549..2a214cf6 100644 --- a/halpybot/packages/utils/utils.py +++ b/halpybot/packages/utils/utils.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - utils.py - miscellaneous utility functions -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/server/__init__.py b/halpybot/server/__init__.py index 952f7439..acaa24d3 100644 --- a/halpybot/server/__init__.py +++ b/halpybot/server/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for the Bot Server module -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/server/auth.py b/halpybot/server/auth.py index f134194e..02b380ef 100644 --- a/halpybot/server/auth.py +++ b/halpybot/server/auth.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - auth.py - Bare bones HAPIC authentication system -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/server/rank_change.py b/halpybot/server/rank_change.py index 227d6012..3e87ee7f 100644 --- a/halpybot/server/rank_change.py +++ b/halpybot/server/rank_change.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - rank_change.py - Handler for Seal vhost changes requested by the API -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -12,6 +10,7 @@ """ from aiohttp import web +from loguru import logger from .server import APIConnector from .auth import authenticate from ..packages.database import DatabaseConnection, NoDatabaseConnection @@ -52,6 +51,7 @@ async def tail(request): await botclient.rawmsg("hs", "SETALL", i[0], vhost) raise web.HTTPOk except NoDatabaseConnection: + logger.exception("No database connection, unable to TAIL.") raise web.HTTPServiceUnavailable from NoDatabaseConnection diff --git a/halpybot/server/server.py b/halpybot/server/server.py index 01c1f668..9ec8d68e 100644 --- a/halpybot/server/server.py +++ b/halpybot/server/server.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - server.py - Hull Seals API -> HalpyBOT server -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/halpybot/server/server_announcer.py b/halpybot/server/server_announcer.py index fe6bde44..f1538e43 100644 --- a/halpybot/server/server_announcer.py +++ b/halpybot/server/server_announcer.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - server_announcer.py - Handler for announcements requested by the API -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/start.py b/start.py index a17ab3af..aff92200 100644 --- a/start.py +++ b/start.py @@ -1,12 +1,10 @@ """ -HalpyBOT v1.6 - > For the Hull Seals, with a boot to the head Rixxan start.py - HalpyBOT startup script -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -39,7 +37,7 @@ if not path.exists(logFolder): mkdir(logFolder) except PermissionError: - print("Unable to create log folder. Does this user have appropriate permissions?") + logger.exception("Unable to create log folder. Does this user have appropriate permissions?") sys.exit() # Set the log format diff --git a/tests/__init__.py b/tests/__init__.py index 6aad20f5..feeb31df 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - __init__.py - Initilization for HalpyBOT Tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/conftest.py b/tests/conftest.py index 82d2f18f..aef9e49d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - conftest.py - Fixture Developement Workshop -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/fixtures/mock_edsm.py b/tests/fixtures/mock_edsm.py index d3253075..efc47010 100644 --- a/tests/fixtures/mock_edsm.py +++ b/tests/fixtures/mock_edsm.py @@ -1,10 +1,8 @@ """ -HalpyBOT v1.6 - mock_edsm.py - Elite: Dangerous Star Map API interface mock instance Taking the call so we don't have to ping EDSM. Yay voicemail! -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -108,6 +106,19 @@ def mock_api_server_fx(): "url": "https://www.edsm.net/en/user/profile/id/58048/cmdr/Rixxan", } ) + mock.expect_request( + "/api-logs-v1/get-position", + query_string="commanderName=Abildgaard+Jadrake&showCoordinates=1", + ).respond_with_json( + { + "msgnum": 100, + "msg": "OK", + "system": None, + "firstDiscover": None, + "date": None, + } + ) + mock.expect_request( "/api-logs-v1/get-position", query_string="commanderName=Praisehalpydamnwhyisthisnotacmdrnam&showCoordinates=1", diff --git a/tests/fixtures/mock_halpy.py b/tests/fixtures/mock_halpy.py index 909ba059..1cf4dc00 100644 --- a/tests/fixtures/mock_halpy.py +++ b/tests/fixtures/mock_halpy.py @@ -10,11 +10,9 @@ Copyright (c) 2018, The Fuel Rats Mischief All rights reserved. -HalpyBOT v1.6 - mock_halpy.py - A Fully-Lobotimized Version of HalpyBOT for Tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_auth.py b/tests/test_auth.py index a392afbd..7d53ffbd 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_auth.py - Server authentication module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_checks.py b/tests/test_checks.py index 2c367a3a..489dca2f 100644 --- a/tests/test_checks.py +++ b/tests/test_checks.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_checks.py - Permission check module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_commands.py b/tests/test_commands.py index e27b5455..1b03553f 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_commands.py - What are your orders, Captain? -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -607,6 +605,23 @@ async def test_locate(bot_fx, mock_api_server_fx): } +@pytest.mark.asyncio +async def test_locate_malformed_response(bot_fx, mock_api_server_fx): + """Test the locate command when EDSM gives a malformed or incomplete response""" + if config["EDSM"]["uri"] != "http://127.0.0.1:4000": + pytest.skip("Invalid EDSM IP Given") + await Commands.invoke_from_message( + bot=bot_fx, + channel="#bot-test", + sender="generic_seal", + message=f"{config['IRC']['commandprefix']}locate Abildgaard Jadrake", + ) + assert bot_fx.sent_messages[0] == { + "message": "Received a reply from EDSM about Abildgaard Jadrake, but could not process the return.", + "target": "#bot-test", + } + + @pytest.mark.asyncio async def test_locate_2(bot_fx, mock_api_server_fx): """Test the locate command with no arguments""" diff --git a/tests/test_config.py b/tests/test_config.py index fb5d6af7..61315a5f 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_config.py - Configuration File module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_database.py b/tests/test_database.py index 804d18a4..e2f351fc 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_database.py - Database connection initialization module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_delayed.py b/tests/test_delayed.py index 988c661c..b579c8a0 100644 --- a/tests/test_delayed.py +++ b/tests/test_delayed.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_delayed.py - DDatabase interaction for Delayed Board module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_edsm.py b/tests/test_edsm.py index 963b3a99..38ae6800 100644 --- a/tests/test_edsm.py +++ b/tests/test_edsm.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_edsm.py - Elite: Dangerous Star Map API interface module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License @@ -29,6 +27,7 @@ EDSMConnectionError, sys_cleaner, NoNearbyEDSM, + EDSMReturnError, ) # noinspection PyUnresolvedReferences @@ -138,6 +137,13 @@ async def test_location(): assert location.system == "Pleiades Sector HR-W d1-79" +@pytest.mark.asyncio +async def test_location_malformed(): + """Test that the Commander system can process a malformed EDSM return""" + with pytest.raises(EDSMReturnError): + await Commander.location("Abildgaard Jadrake") + + @pytest.mark.asyncio async def test_landmark(): """Test that the Landmark system operates correctly""" diff --git a/tests/test_facts.py b/tests/test_facts.py index 439b5e58..106ed0ea 100644 --- a/tests/test_facts.py +++ b/tests/test_facts.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_facts.py - Fact handler tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_logging.py b/tests/test_logging.py index 97546436..7c8e685d 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_logging.py - Logging System Module Unit Tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_seals.py b/tests/test_seals.py index 90dcc419..50954078 100644 --- a/tests/test_seals.py +++ b/tests/test_seals.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_seals.py - Fetching information about a registered user module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_server.py b/tests/test_server.py index 635592fb..a69d2159 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_server.py - Permission check module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License diff --git a/tests/test_utils.py b/tests/test_utils.py index 23ae8286..3317b64c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,9 +1,7 @@ """ -HalpyBOT v1.6 - test_utils.py - miscellaneous utility functions module tests -Copyright (c) 2022 The Hull Seals, +Copyright (c) The Hull Seals, All rights reserved. Licensed under the GNU General Public License