Compare commits

...

9 Commits

Author SHA1 Message Date
aa3996bbd9 fix deployment script
All checks were successful
Garfbot CI/CD Deployment / Deploy (push) Successful in 6s
2025-06-07 13:43:14 -05:00
c71a55da2f fix deployment script
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 4s
2025-06-07 13:38:16 -05:00
7f0ce32eea fix deployment script
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 4s
2025-06-07 13:34:59 -05:00
e34469a755 Merge pull request 'move help and update dockerfile' (#7) from help into main
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 2m3s
Reviewed-on: #7

looks good
2025-06-07 18:08:30 +00:00
e1707e789c move help and update dockerfile 2025-06-07 13:07:26 -05:00
a53a81a180 Merge pull request 'weather' (#6) from weather into main
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 4s
Reviewed-on: #6
2025-06-07 01:19:07 +00:00
4d2a0dc2d9 add weather 2025-06-06 20:14:50 -05:00
6ec931265f Merge remote-tracking branch 'origin/main' into weather 2025-06-06 18:37:23 -05:00
4b077e3fd9 weather 2025-06-03 17:54:40 -05:00
7 changed files with 293 additions and 76 deletions

View File

@ -8,21 +8,20 @@ jobs:
Deploy:
container:
volumes:
- /home/crate/garfbot:/workspace/crate/garfbot/deploy
- /home/crate/garfbot:/workspace/crate/garfbot/garfbot
steps:
- name: Pull Garfbot and restart container
run: |
cd /workspace/crate/garfbot/deploy
git pull origin main
- name: Pull GarfBot and re-deploy
run: |
cd /workspace/crate/garfbot/garfbot
git pull origin main
CHANGED=$(git diff --name-only HEAD~1 HEAD)
CHANGED=$(git diff --name-only HEAD~1 HEAD)
if echo "$CHANGED" | grep -qE "(Dockerfile|requirements\.txt|docker-compose\.yml)"; then
docker stop garfbot
docker rm garfbot
docker build -t git.crate.zip/crate/garfbot:latest .
docker compose up -d -p garfbot
else
docker restart garfbot
fi
if echo "$CHANGED" | grep -qE "(Dockerfile|requirements\.txt|docker-compose\.yml|\.gitea/workflows/deploy\.yaml)"; then
docker compose down
docker build -t git.crate.zip/crate/garfbot:latest .
docker compose up -d
else
docker restart garfbot
fi

View File

@ -1,4 +1,4 @@
FROM python:3.11.10-alpine
FROM python:alpine
WORKDIR /usr/src/app
RUN apk update && \

View File

@ -3,6 +3,7 @@ import asyncio
import discord
from garfpy import (
help,
logger,
IPUtils,
aod_message,
@ -10,6 +11,7 @@ from garfpy import (
Kroger,
GarfAI,
GarfbotRespond,
WeatherAPI,
)
@ -28,6 +30,7 @@ garf_respond = GarfbotRespond()
garfield = GarfAI()
iputils = IPUtils()
kroger = Kroger()
weather = WeatherAPI()
@garfbot.event
@ -107,64 +110,15 @@ 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)
embed.add_field(
name="hey garfield `prompt`", value="*Responds with text.*", inline=True
)
embed.add_field(
name="garfpic `prompt`", value="*Responds with an image.*", inline=True
)
embed.add_field(
name="garfping `target`",
value="*Responds with iputils-ping result from target.*",
inline=True,
)
embed.add_field(
name="garfdns `target`",
value="*Responds with dns lookup result from target.*",
inline=True,
)
embed.add_field(
name="garfhack `target`",
value="*Responds with nmap scan result from target.*",
inline=True,
)
embed.add_field(
name="garfwiki `query`",
value="*Garfbot looks up a wikipedia article and will summarize it for you.*",
inline=True,
)
embed.add_field(
name="garfshop `item` `zip`",
value="*Responds with 10 grocery items from the nearest Kroger location, cheapest first.*",
inline=True,
)
embed.add_field(
name="garfqr `text`",
value="*Create a QR code for any string up to 1000 characters.*",
inline=True,
)
embed.add_field(
name="garfbot response `add` `trigger` `response`",
value='*Add a GarfBot auto response for your server. Use "quotes" if you like.*',
inline=True,
)
embed.add_field(
name="garfbot response `remove` `trigger`",
value="*Remove a GarfBot auto response for your server.*",
inline=True,
)
embed.add_field(
name="garfbot response `list`",
value="*List current GarfBot auto responses for your server.*",
inline=True,
)
embed.add_field(
name="garfbot help", value="*Show a list of these commands.*", inline=True
)
await message.channel.send(embed=embed)
await help(message)
# Army of Dawn Server only!!
elif message.guild and message.guild.id == 719605634772893757:

View File

@ -1,6 +1,7 @@
# garfpy/__init__.py
from .log import logger
from .help import help
from .kroger import Kroger
from .kroger import Kroger
from .garfai import GarfAI
@ -8,3 +9,4 @@ from .respond import GarfbotRespond
from .aod import aod_message
from .qr import generate_qr
from .iputils import IPUtils
from .weather import WeatherAPI

View File

@ -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:

60
garfpy/help.py Normal file
View File

@ -0,0 +1,60 @@
import discord
async def help(message):
embed = discord.Embed(title="**Need help?**", color=0x4D4D4D)
embed.add_field(
name="hey garfield `prompt`", value="*Responds with text.*", inline=True
)
embed.add_field(
name="garfpic `prompt`", value="*Responds with an image.*", inline=True
)
embed.add_field(
name="garfping `target`",
value="*Responds with iputils-ping result from target.*",
inline=True,
)
embed.add_field(
name="garfdns `target`",
value="*Responds with dns lookup result from target.*",
inline=True,
)
embed.add_field(
name="garfhack `target`",
value="*Responds with nmap scan result from target.*",
inline=True,
)
embed.add_field(
name="garfwiki `query`",
value="*Garfbot looks up a wikipedia article and will summarize it for you.*",
inline=True,
)
embed.add_field(
name="garfshop `item` `zip`",
value="*Responds with 10 grocery items from the nearest Kroger location, cheapest first.*",
inline=True,
)
embed.add_field(
name="garfqr `text`",
value="*Create a QR code for any string up to 1000 characters.*",
inline=True,
)
embed.add_field(
name="garfbot response `add` `trigger` `response`",
value='*Add a GarfBot auto response for your server. Use "quotes" if you like.*',
inline=True,
)
embed.add_field(
name="garfbot response `remove` `trigger`",
value="*Remove a GarfBot auto response for your server.*",
inline=True,
)
embed.add_field(
name="garfbot response `list`",
value="*List current GarfBot auto responses for your server.*",
inline=True,
)
embed.add_field(
name="garfbot help", value="*Show a list of these commands.*", inline=True
)
await message.channel.send(embed=embed)

202
garfpy/weather.py Normal file
View File

@ -0,0 +1,202 @@
import discord
import aiohttp
import config
from garfpy import logger
class WeatherAPI:
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 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:
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 = {
**location_params,
"appid": self.api_key,
"units": units,
}
try:
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:
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
async def weather(self, location):
weather_data = await self.get_weather(location)
embed = self.weather_embed(weather_data)
return embed