Compare commits

...

24 Commits

Author SHA1 Message Date
4b077e3fd9 weather 2025-06-03 17:54:40 -05:00
9d5765c492 fix gitea deploy pipeline
All checks were successful
Garfbot CI/CD Deployment / Deploy (push) Successful in 16s
2025-06-02 17:59:32 -05:00
8216af5f2a deploy test
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 3s
2025-06-02 13:52:24 -05:00
219b1745da deploy test
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 2s
2025-06-02 13:51:46 -05:00
d570069e6b test
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 3s
2025-06-02 13:51:06 -05:00
f267526411 deploy test
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 3s
2025-06-02 13:49:49 -05:00
86fa47d259 remove docker socket mount
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 5s
2025-06-02 13:47:57 -05:00
888e647e1f add gitea deploy script
Some checks failed
Garfbot CI/CD Deployment / Deploy (push) Failing after 1s
2025-06-02 13:06:23 -05:00
c1beb8374e fix qr sendfile 2025-05-25 04:15:08 -05:00
48a7033ba6 add pillow 2025-05-25 04:11:57 -05:00
9e436d8d78 fix qrcode call async 2025-05-25 04:08:18 -05:00
d8c286bb2d add qrcode 2025-05-25 04:04:32 -05:00
a081edfa97 add qr code gen 2025-05-25 04:02:34 -05:00
716e30e2b1 fix chat gen 2025-05-23 20:46:26 -05:00
73499a9586 oops 2025-05-23 13:35:19 -05:00
e3a762fe10 await chat gen 2025-05-23 13:33:02 -05:00
842b592bfc garfsum 2025-05-23 13:30:36 -05:00
f9f88be8af wiki except 2025-05-23 11:41:26 -05:00
5c8696216e fix wiki init 2025-05-22 17:12:56 -05:00
aa16064d17 fix wikisum import 2025-05-22 17:11:39 -05:00
ffd642fd2a add wiki stuff 2025-05-22 17:09:48 -05:00
3945eb6763 add wiki 2025-05-22 16:53:37 -05:00
0438b55ce6 remove tenor gif bs 2025-05-22 16:46:22 -05:00
8fb4e089be Update README.md 2024-12-28 05:08:50 +00:00
10 changed files with 278 additions and 36 deletions

View File

@ -0,0 +1,28 @@
name: Garfbot CI/CD Deployment
on:
push:
branches: [ main ]
jobs:
Deploy:
container:
volumes:
- /home/crate/garfbot:/workspace/crate/garfbot/deploy
steps:
- name: Pull Garfbot and restart container
run: |
cd /workspace/crate/garfbot/deploy
git pull origin main
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 run -d --restart always -v $PWD:/usr/src/app --name garfbot git.crate.zip/crate/garfbot:latest
else
docker restart garfbot
fi

View File

@ -12,5 +12,8 @@ RUN pip3 install discord
RUN pip3 install openai
RUN pip3 install aiohttp
RUN pip3 install requests
RUN pip3 install wikipedia
RUN pip3 install pillow
RUN pip3 install qrcode
CMD [ "python", "garfmain.py" ]

View File

@ -16,7 +16,7 @@ Responds with iputils-ping result from target.
`garfpic {target}`
Responds with dns lookup result from target.
`garfpic {target}`
`garfhack {target}`
Responds with nmap scan result from target.
`garfshop {item} {zip}`
@ -36,7 +36,6 @@ Add your various API tokens:
```python
GARFBOT_TOKEN = "Discord API token"
OPENAI_TOKEN = "OpenAI API token"
GIF_TOKEN = "tenor.com API token"
```
I recommend building a docker image using the included DockerFile as a template. Run the container binding /usr/src/app to GarfBot's CWD:

View File

@ -1,5 +1,4 @@
import config
# import random
import asyncio
import discord
import subprocess
@ -7,8 +6,8 @@ import subprocess
from garfpy import(
logger, is_private,
kroger_token, find_store, search_product,
picture_time, process_image_requests, generate_chat,
aod_message)
garfpic, process_image_requests, generate_chat,
aod_message, wikisum, generate_qr)
gapikey = config.GIF_TOKEN
@ -38,8 +37,11 @@ async def on_message(message):
return
if message.content.lower().startswith("hey garfield") or isinstance(message.channel, discord.DMChannel):
user = message.author.name
server = message.guild.name if message.guild else "Direct Message"
question = message.content[12:] if message.content.lower().startswith("hey garfield") else message.content
answer = await generate_chat(question)
logger.info(f"Chat Request - User: {user}, Server: {server}, Prompt: {question}")
await message.channel.send(answer)
if message.content.lower().startswith('garfpic '):
@ -48,17 +50,25 @@ async def on_message(message):
prompt = message.content[8:]
logger.info(f"Image Request - User: {user}, Server: {server}, Prompt: {prompt}")
await message.channel.send(f"`Please wait... image generation queued: {prompt}`")
await picture_time(message, prompt)
await garfpic(message, prompt)
# if message.content.lower() == "lasagna":
# await send_gif(message, "garfield lasagna")
if message.content.lower().startswith('garfwiki '):
search_term = message.content[9:]
summary = await wikisum(search_term)
await message.channel.send(summary)
# if message.content.lower() == "monday":
# await send_gif(message, "garfield monday")
# if message.content.lower().startswith("garfgif "):
# search_term = message.content[8:]
# await send_gif(message, search_term)
if message.content.lower().startswith('garfqr '):
text = message.content[7:]
if len(text) > 1000:
await mesage.channel.send("❌ Text too long! Maximum 1000 characters.")
else:
try:
qr_code = await generate_qr(text)
sendfile = discord.File(fp=qr_code, filename="qrcode.png")
await message.channel.send(file=sendfile)
except Exception as e:
logger.error(e)
await message.channel.send(e)
if message.content.lower().startswith("garfping "):
try:
@ -137,25 +147,6 @@ async def on_message(message):
await aod_message(garfbot, message)
# # GarfGifs
# @garfbot.event
# async def send_gif(message, search_term):
# lmt = 50
# ckey = "garfbot"
# r = requests.get(f"https://tenor.googleapis.com/v2/search?q={search_term}&key={gapikey}&client_key={ckey}&limit={lmt}")
# if r.status_code == 200:
# top_50gifs = json.loads(r.content)
# gif_url = random.choice(top_50gifs["results"])["itemurl"]
# logger.info(gif_url)
# # logger.info(gif_url)
# try:
# await message.channel.send(gif_url)
# except KeyError:
# await message.channel.send("Oops, something went wrong.")
# else:
# await message.channel.send(f"`Oops, something went wrong. Error code: {r.status_code}`")
async def garfbot_connect():
while True:
try:

View File

@ -4,9 +4,11 @@ from .kroger import(
kroger_token, find_store, search_product
)
from .garfai import(
picture_time,
garfpic,
process_image_requests,
generate_chat
)
from .iputils import is_private
from .aod import aod_message
from .wiki import wikisum
from .qr import generate_qr

View File

@ -12,10 +12,9 @@ openaikey = config.OPENAI_TOKEN
txtmodel = config.TXT_MODEL
imgmodel = config.IMG_MODEL
# GarfPics
image_request_queue = asyncio.Queue()
async def picture_time(message, prompt):
async def garfpic(message, prompt):
await image_request_queue.put({'message': message, 'prompt': prompt})
async def generate_image(prompt):

63
garfpy/qr.py Normal file
View File

@ -0,0 +1,63 @@
import qrcode
from io import BytesIO
def calculate_qr_settings(text):
text_length = len(text)
if text_length <= 25:
version = 1
box_size = 12
elif text_length <= 47:
version = 2
box_size = 10
elif text_length <= 77:
version = 3
box_size = 8
elif text_length <= 114:
version = 4
box_size = 7
elif text_length <= 154:
version = 5
box_size = 6
elif text_length <= 195:
version = 6
box_size = 5
elif text_length <= 224:
version = 7
box_size = 5
elif text_length <= 279:
version = 8
box_size = 4
elif text_length <= 335:
version = 9
box_size = 4
elif text_length <= 395:
version = 10
box_size = 3
else:
version = None
box_size = 3
return version, box_size
async def generate_qr(text):
version, box_size = calculate_qr_settings(text)
qr = qrcode.QRCode(
version=version,
error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=box_size,
border=4,
)
qr.add_data(text)
qr.make(fit=True)
qr_image = qr.make_image(fill_color="black", back_color="white")
img_buffer = BytesIO()
qr_image.save(img_buffer, format='PNG')
img_buffer.seek(0)
return img_buffer

142
garfpy/weather.py Normal file
View File

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

12
garfpy/wiki.py Normal file
View File

@ -0,0 +1,12 @@
import wikipedia
from garfpy import generate_chat
async def wikisum(search_term):
try:
summary = wikipedia.summary(search_term)
garfsum = await generate_chat(f"Please summarize in your own words: {summary}")
return garfsum
except Exception as e:
return e

View File

@ -2,3 +2,6 @@ discord.py
openai
aiohttp
requests
wikipedia
pillow
qrcode