From 4b077e3fd9e2e4bb8e7921219664bb54184008e0 Mon Sep 17 00:00:00 2001 From: crate Date: Tue, 3 Jun 2025 17:54:40 -0500 Subject: [PATCH 1/2] weather --- garfpy/weather.py | 142 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 garfpy/weather.py diff --git a/garfpy/weather.py b/garfpy/weather.py new file mode 100644 index 0000000..14b893f --- /dev/null +++ b/garfpy/weather.py @@ -0,0 +1,142 @@ +import discord +import requests +import json + +class WeatherAPI: + def __init__(self, api_key): + self.api_key = api_key + 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' + """ + params = { + 'zip': f'{zip_code},{country_code}', + '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}") + return None + +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=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 -- 2.47.2 From 4d2a0dc2d976bf5814473138ebb614e33771a6ac Mon Sep 17 00:00:00 2001 From: crate Date: Fri, 6 Jun 2025 20:14:50 -0500 Subject: [PATCH 2/2] add weather --- garfmain.py | 8 ++ garfpy/__init__.py | 1 + garfpy/garfai.py | 6 +- garfpy/weather.py | 321 +++++++++++++++++++++++++++------------------ 4 files changed, 203 insertions(+), 133 deletions(-) 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 -- 2.47.2