diff --git a/poetry.lock b/poetry.lock index b262c3d9..9d57f1eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -74,7 +74,7 @@ pyreadline = "*" [[package]] name = "idna" -version = "3.0" +version = "3.1" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false @@ -112,7 +112,7 @@ marker = "sys_platform == \"win32\"" [[package]] name = "royalnet" -version = "6.0.0a33" +version = "6.0.0a36" description = "A multipurpose bot and web framework" category = "main" optional = false @@ -174,7 +174,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.0" python-versions = "^3.9" -content-hash = "227a77d4af0a59cb4289ac3e29d342788bafe838fbabc5e566383e518f86c5cf" +content-hash = "ef6d764f995f9579e32eef84b9eb802a5b97677893a5870c99b81d4af0d945ea" [metadata.files] aiohttp = [ @@ -237,8 +237,8 @@ humanfriendly = [ {file = "humanfriendly-9.1.tar.gz", hash = "sha256:066562956639ab21ff2676d1fda0b5987e985c534fc76700a19bd54bcb81121d"}, ] idna = [ - {file = "idna-3.0-py2.py3-none-any.whl", hash = "sha256:320229aadbdfc597bc28876748cc0c9d04d476e0fe6caacaaddea146365d9f63"}, - {file = "idna-3.0.tar.gz", hash = "sha256:c9a26e10e5558412384fac891eefb41957831d31be55f1e2c98ed97a70abb969"}, + {file = "idna-3.1-py3-none-any.whl", hash = "sha256:5205d03e7bcbb919cc9c19885f9920d622ca52448306f2377daede5cf3faac16"}, + {file = "idna-3.1.tar.gz", hash = "sha256:c5b02147e01ea9920e6b0a3f1f7bb833612d507592c837a6c49552768f4054e1"}, ] multidict = [ {file = "multidict-5.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f"}, @@ -309,8 +309,8 @@ pyreadline = [ {file = "pyreadline-2.1.zip", hash = "sha256:4530592fc2e85b25b1a9f79664433da09237c1a270e4d78ea5aa3a2c7229e2d1"}, ] royalnet = [ - {file = "royalnet-6.0.0a33-py3-none-any.whl", hash = "sha256:b8455febca99b8e5d2a9260ea4d7694975c65182fbdd4a6159411b6830fa0796"}, - {file = "royalnet-6.0.0a33.tar.gz", hash = "sha256:7b9c18c918dae94db437640bf044fb20ff6180b879f45aa067b68abdb598c5d6"}, + {file = "royalnet-6.0.0a36-py3-none-any.whl", hash = "sha256:8cfd930b774db4041a4aa17d3e44528ae414e05eae15c3036ceebf856b54ea39"}, + {file = "royalnet-6.0.0a36.tar.gz", hash = "sha256:fb2f89e360af1668500b690071aba37d8fbae6ebf813f821171030b1721ce0c3"}, ] sqlalchemy = [ {file = "SQLAlchemy-1.3.22-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:61628715931f4962e0cdb2a7c87ff39eea320d2aa96bd471a3c293d146f90394"}, diff --git a/pyproject.toml b/pyproject.toml index b609eb08..e0a9e9d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ license = "AGPL-3.0-or-later" [tool.poetry.dependencies] python = "^3.9" -royalnet = "6.0.0a33" +royalnet = "^6.0.0a36" coloredlogs = "^15.0" aiohttp = "^3.7.3" diff --git a/royalpack/commands/ahnonlosoio.py b/royalpack/commands/ahnonlosoio.py index a394513f..17f17202 100644 --- a/royalpack/commands/ahnonlosoio.py +++ b/royalpack/commands/ahnonlosoio.py @@ -2,11 +2,11 @@ import royalnet.engineer as engi @engi.PartialCommand.new(syntax="") -def ahnonlosoio(_sentry: engi.Sentry, _msg: engi.Message, **__): +def ahnonlosoio(*, _sentry: engi.Sentry, _msg: engi.Message, **__): """ Ah, non lo so io! """ - await _msg.send_reply(r"¯\_(ツ)_/¯ Ah, non lo so io!") + await _msg.send_reply(text=r"¯\_(ツ)_/¯ Ah, non lo so io!") __all__ = ("ahnonlosoio",) diff --git a/royalpack/commands/cat.py b/royalpack/commands/cat.py new file mode 100644 index 00000000..4f9f69d2 --- /dev/null +++ b/royalpack/commands/cat.py @@ -0,0 +1,96 @@ +# Special imports +from __future__ import annotations +import royalnet.royaltyping as t + +# External imports +import aiohttp +import royalnet.engineer as engi +import logging +import io + +# Internal imports +# from . import something + +# Special global objects +log = logging.getLogger(__name__) + + +# Code +@engi.PartialCommand.new(syntax="") +def cat(*, _sentry: engi.Sentry, _msg: engi.Message, **__): + """ + Send a cat in the chat! 🐈 + """ + + log.debug("Creating a new HTTP session") + async with aiohttp.ClientSession() as session: + + log.info("Making a GET request to The Cat API Image Search") + async with session.get("https://api.thecatapi.com/v1/images/search") as response: + + log.debug("Ensuring the request was successful") + if response.status >= 400: + log.error(f"The Cat API returned an HTTP error: {response.status}") + await _msg.send_reply( + text="⚠️ Couldn't request a cat from https://thecatapi.com :(" + ) + return + + log.debug("Reading the JSON received from The Cat API") + try: + result = await response.json() + except aiohttp.ContentTypeError: + log.error(f"Couldn't decode received JSON from The Cat API") + await _msg.send_reply( + text="⚠️ Couldn't understand what the cat from https://thecatapi.com was saying :(" + ) + return + + # Example result: + # [ + # { + # "breeds": [], + # "id": "MjAzMjY3MQ", + # "url": "https://cdn2.thecatapi.com/images/MjAzMjY3MQ.jpg", + # "width": 557, + # "height": 724 + # } + # ] + + log.debug("Ensuring at least one image was received") + if len(result) == 0: + log.error("Didn't receive any image from The Cat API") + await _msg.send_reply( + text="⚠️ I couldn't find any cats at https://thecatapi.com :(" + ) + return + + # Select the first image received + selected_cat = result[0] + log.debug(f"Selected {selected_cat!r}") + + log.debug("Ensuring an image url is available") + if "url" not in selected_cat: + log.error("Image received from The Cat API did not have any URL") + await _msg.send_reply( + text="⚠️ I found a cat at https://thecatapi.com, but I couldn't find its image :(" + ) + return + + # Download the cat image + log.info("Making a GET request to retrieve a The Cat API image") + async with session.get(selected_cat["url"]) as response: + + log.debug("Reading image bytes into memory") + img = io.BytesIO() + while img_data := response.content.read(8192): + img.write(img_data) + + log.debug("Sending image in the chat") + await _msg.send_reply(files=[img]) + + +# Objects exported by this module +__all__ = ( + "cat", +) diff --git a/royalpack/commands/ping.py b/royalpack/commands/ping.py index 11f0890c..409d31b1 100644 --- a/royalpack/commands/ping.py +++ b/royalpack/commands/ping.py @@ -2,11 +2,11 @@ import royalnet.engineer as engi @engi.PartialCommand.new(syntax="") -def ping(_sentry: engi.Sentry, _msg: engi.Message, **__): +def ping(*, _sentry: engi.Sentry, _msg: engi.Message, **__): """ A way to check if the bot is working: it will always reply to this command with "🏓 Pong!". """ - await _msg.send_reply("🏓 Pong!") + await _msg.send_reply(text="🏓 Pong!") __all__ = ("ping",) diff --git a/royalpack/commands/ship.py b/royalpack/commands/ship.py new file mode 100644 index 00000000..42dca09a --- /dev/null +++ b/royalpack/commands/ship.py @@ -0,0 +1,59 @@ +# Special imports +from __future__ import annotations +import royalnet.royaltyping as t + +# External imports +import royalnet.engineer as engi +import logging +import re + +# Internal imports +# from . import something + +# Special global objects +log = logging.getLogger(__name__) + + +# Code +@engi.PartialCommand.new(syntax=r"(?P[A-Za-z]+)[\s+&]+(?P[A-Za-z]+)") +def ship(*, _sentry: engi.Sentry, _msg: engi.Message, first: str, second: str, **__): + """ + Ship two names together! 💞 + """ + log.info(f"Shipping: {first!r} + {second!r}") + + # Convert the names to lowercase + first = first.lower() + second = second.lower() + + log.debug(f"Lowercased: {first!r} + {second!r}") + + # Get all letters until the first vowel, included + first_match = re.search(r"^[A-Za-z][^aeiouAEIOU]*[aeiouAEIOU]?", first) + # Get all letters from the second to last vowel, excluded + second_match = re.search(r"[^aeiouAEIOU]*[aeiouAEIOU]?[A-Za-z]$", second) + + log.debug(f"Matches: {first_match!r} + {second_match!r}") + + # Get the matched characters if the matches were successful, or cut the names in half if they weren't + first = first_match.group(0) if first_match else first[:(len(first) // 2)] + second = second_match.group(0) if second_match else second[(len(second) // 2):] + + log.debug(f"Cropped: {first!r} + {second!r}") + + # Combine the two parts + combined = f"{first}{second}" + + log.info(f"Combined: {combined!r}") + + # Send the message to the chat + log.debug(f"Sending ship to the chat...") + await _msg.send_reply( + text=f"💞 {first.capitalize()} + {second.capitalize()} = {combined.capitalize()}" + ) + + +# Objects exported by this module +__all__ = ( + "ship", +)