diff --git a/garfmain.py b/garfmain.py index ed8d9fb..7cab5d3 100644 --- a/garfmain.py +++ b/garfmain.py @@ -10,6 +10,7 @@ from garfpy import ( Kroger, GarfAI, GarfbotRespond, + WeatherAPI, ) @@ -28,6 +29,7 @@ garf_respond = GarfbotRespond() garfield = GarfAI() iputils = IPUtils() kroger = Kroger() +weather = WeatherAPI() @garfbot.event @@ -107,6 +109,12 @@ async def on_message(message): ) await garfield.garfpic(message, prompt) + # Weather + elif lower.startswith("garfbot weather "): + location = lower[16:] + embed = await weather.weather(location) + await message.channel.send(embed=embed) + # GarfBot help elif lower.strip() == "garfbot help": embed = discord.Embed(title="**Need help?**", color=0x4D4D4D) diff --git a/garfpy/__init__.py b/garfpy/__init__.py index a11fd0b..16147aa 100644 --- a/garfpy/__init__.py +++ b/garfpy/__init__.py @@ -8,3 +8,4 @@ from .respond import GarfbotRespond from .aod import aod_message from .qr import generate_qr from .iputils import IPUtils +from .weather import WeatherAPI \ No newline at end of file diff --git a/garfpy/garfai.py b/garfpy/garfai.py index 2e3c883..b3f81ec 100644 --- a/garfpy/garfai.py +++ b/garfpy/garfai.py @@ -48,11 +48,11 @@ class GarfAI: async with session.get(image_url) as resp: if resp.status == 200: image_data = await resp.read() - ram_image = io.BytesIO(image_data) - ram_image.seek(0) + image = io.BytesIO(image_data) + image.seek(0) timestamp = message.created_at.strftime("%Y%m%d%H%M%S") filename = f"{timestamp}_generated_image.png" - sendfile = discord.File(fp=ram_image, filename=filename) + sendfile = discord.File(fp=image, filename=filename) try: await message.channel.send(file=sendfile) except Exception as e: diff --git a/garfpy/weather.py b/garfpy/weather.py index 14b893f..b152b7b 100644 --- a/garfpy/weather.py +++ b/garfpy/weather.py @@ -1,142 +1,203 @@ import discord -import requests -import json +import aiohttp +import config +from garfpy import logger + class WeatherAPI: - def __init__(self, api_key): - self.api_key = api_key + def __init__(self, api_key=None): + self.api_key = api_key or config.WEATHER_TOKEN self.base_url = "https://api.openweathermap.org/data/2.5/weather" - - def get_weather_by_zip(self, zip_code, country_code='US', units='metric'): - """ - Get weather data by zip code - - Args: - zip_code (str): ZIP code - country_code (str): Country code (default: 'US') - units (str): 'metric', 'imperial', or 'standard' - """ + + def parse_location(self, location): + location = location.strip().lower() + + if location.isdigit(): + if len(location) == 5: + return {"zip": f"{location},US"} + else: + return {"zip": location} + + parts = location.split() + + if len(parts) == 1: + return {"q": f"{parts[0]},US"} + + elif len(parts) == 2: + city, second = parts + + if len(second) == 2 and second.upper() not in [ + "AK", + "AL", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VT", + "WA", + "WI", + "WV", + "WY", + "DC", + "AS", + "GU", + "MP", + "PR", + "VI", + ]: + return {"q": f"{city},{second.upper()}"} + else: + return {"q": f"{city},{second},US"} + + elif len(parts) == 3: + city, state, country = parts + return {"q": f"{city},{state},{country.upper()}"} + + else: + # Check if last part looks like country code + if len(parts[-1]) == 2: + city_parts = parts[:-1] + country = parts[-1] + city_name = " ".join(city_parts) + return {"q": f"{city_name},{country.upper()}"} + + elif len(parts) >= 2 and len(parts[-1]) == 2 and len(parts[-2]) <= 2: + city_parts = parts[:-2] + state = parts[-2] + country = parts[-1] + city_name = " ".join(city_parts) + return {"q": f"{city_name},{state},{country.upper()}"} + + else: + city_name = " ".join(parts) + return {"q": f"{city_name},US"} + + async def get_weather(self, location, units="metric"): + location_params = self.parse_location(location) + params = { - 'zip': f'{zip_code},{country_code}', - 'appid': self.api_key, - 'units': units + **location_params, + "appid": self.api_key, + "units": units, } - + try: - response = requests.get(self.base_url, params=params) - response.raise_for_status() - return response.json() - - except requests.exceptions.RequestException as e: - print(f"Error fetching weather data: {e}") + async with aiohttp.ClientSession() as session: + async with session.get(self.base_url, params=params) as response: + response.raise_for_status() + return await response.json() + except aiohttp.ClientError as e: + logger.error(f"Error fetching weather data for '{location}': {e}") return None - -def weather_embed(self, weather_data): - if not weather_data: + + def weather_embed(self, weather_data): + if not weather_data: + embed = discord.Embed( + title="āŒ Error", + description="Could not fetch weather data", + color=discord.Color.red(), + ) + return embed + + weather_emojis = { + "clear sky": "ā˜€ļø", + "few clouds": "šŸŒ¤ļø", + "scattered clouds": "ā›…", + "broken clouds": "ā˜ļø", + "shower rain": "šŸŒ¦ļø", + "rain": "šŸŒ§ļø", + "thunderstorm": "ā›ˆļø", + "snow": "ā„ļø", + "mist": "šŸŒ«ļø", + } + + condition = weather_data["weather"][0]["description"].lower() + emoji = weather_emojis.get(condition, "šŸŒ") + embed = discord.Embed( - title="āŒ Error", - description="Could not fetch weather data", - color=discord.Color.red() + title=f"{emoji} Weather in {weather_data['name']}", + description=f"{weather_data['weather'][0]['description'].title()}", + color=discord.Color.blue(), ) + + embed.add_field( + name="šŸŒ”ļø Temperature", + value=f"{weather_data['main']['temp']}°C\nFeels like {weather_data['main']['feels_like']}°C", + inline=True, + ) + + embed.add_field( + name="šŸ’§ Humidity", + value=f"{weather_data['main']['humidity']}%", + inline=True, + ) + + embed.add_field( + name="šŸ—œļø Pressure", + value=f"{weather_data['main']['pressure']} hPa", + inline=True, + ) + + if "wind" in weather_data: + embed.add_field( + name="šŸ’Ø Wind Speed", + value=f"{weather_data['wind']['speed']} m/s", + inline=True, + ) + + if "visibility" in weather_data: + embed.add_field( + name="šŸ‘ļø Visibility", + value=f"{weather_data['visibility'] / 1000} km", + inline=True, + ) + + embed.set_footer( + text=f"Lat: {weather_data['coord']['lat']}, Lon: {weather_data['coord']['lon']}" + ) + return embed - - weather_emojis = { - 'clear sky': 'ā˜€ļø', - 'few clouds': 'šŸŒ¤ļø', - 'scattered clouds': 'ā›…', - 'broken clouds': 'ā˜ļø', - 'shower rain': 'šŸŒ¦ļø', - 'rain': 'šŸŒ§ļø', - 'thunderstorm': 'ā›ˆļø', - 'snow': 'ā„ļø', - 'mist': 'šŸŒ«ļø' - } - - condition = weather_data['weather'][0]['description'].lower() - emoji = weather_emojis.get(condition, 'šŸŒ') - - embed = discord.Embed( - title=f"{emoji} Weather in {weather_data['name']}", - description=f"{weather_data['weather'][0]['description'].title()}", - color=discord.Color.blue() - ) - - embed.add_field( - name="šŸŒ”ļø Temperature", - value=f"{weather_data['main']['temp']}°C\nFeels like {weather_data['main']['feels_like']}°C", - inline=True - ) - - embed.add_field( - name="šŸ’§ Humidity", - value=f"{weather_data['main']['humidity']}%", - inline=True - ) - - embed.add_field( - name="šŸ—œļø Pressure", - value=f"{weather_data['main']['pressure']} hPa", - inline=True - ) - - if 'wind' in weather_data: - embed.add_field( - name="šŸ’Ø Wind Speed", - value=f"{weather_data['wind']['speed']} m/s", - inline=True - ) - - if 'visibility' in weather_data: - embed.add_field( - name="šŸ‘ļø Visibility", - value=f"{weather_data['visibility']/1000} km", - inline=True - ) - - embed.set_footer( - text=f"Lat: {weather_data['coord']['lat']}, Lon: {weather_data['coord']['lon']}" - ) - - return embed -@commands.command(name='weather') -async def weather_command(self, ctx, zip_code: str, country_code: str = 'US'): - """ - Get weather by zip code - Usage: !weather 10001 US - """ - await ctx.typing() - - weather_data = await self.get_weather_by_zip(zip_code, country_code) - embed = self.create_weather_embed(weather_data) - - await ctx.send(embed=embed) - - - - # def display_weather(self, weather_data): - # """Pretty print weather information""" - # if not weather_data: - # print("No weather data available") - # return - - # print(f"\nšŸŒ¤ļø Weather for {weather_data['name']}") - # print(f"Temperature: {weather_data['main']['temp']}°C") - # print(f"Feels like: {weather_data['main']['feels_like']}°C") - # print(f"Condition: {weather_data['weather'][0]['description'].title()}") - # print(f"Humidity: {weather_data['main']['humidity']}%") - # print(f"Pressure: {weather_data['main']['pressure']} hPa") - # if 'visibility' in weather_data: - # print(f"Visibility: {weather_data['visibility']/1000} km") - -# if __name__ == "__main__": -# API_KEY = "x" - -# weather = WeatherAPI(API_KEY) - -# zip_codes = ['10001', '90210', '60601'] - -# for zip_code in zip_codes: -# data = weather.get_weather_by_zip(zip_code) -# weather.display_weather(data) -# print("-" * 40) \ No newline at end of file + async def weather(self, location): + weather_data = await self.get_weather(location) + embed = self.weather_embed(weather_data) + return embed