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 openai
RUN pip3 install aiohttp RUN pip3 install aiohttp
RUN pip3 install requests RUN pip3 install requests
RUN pip3 install wikipedia
RUN pip3 install pillow
RUN pip3 install qrcode
CMD [ "python", "garfmain.py" ] CMD [ "python", "garfmain.py" ]

View File

@ -16,7 +16,7 @@ Responds with iputils-ping result from target.
`garfpic {target}` `garfpic {target}`
Responds with dns lookup result from target. Responds with dns lookup result from target.
`garfpic {target}` `garfhack {target}`
Responds with nmap scan result from target. Responds with nmap scan result from target.
`garfshop {item} {zip}` `garfshop {item} {zip}`
@ -36,7 +36,6 @@ Add your various API tokens:
```python ```python
GARFBOT_TOKEN = "Discord API token" GARFBOT_TOKEN = "Discord API token"
OPENAI_TOKEN = "OpenAI 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: 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 config
# import random
import asyncio import asyncio
import discord import discord
import subprocess import subprocess
@ -7,8 +6,8 @@ import subprocess
from garfpy import( from garfpy import(
logger, is_private, logger, is_private,
kroger_token, find_store, search_product, kroger_token, find_store, search_product,
picture_time, process_image_requests, generate_chat, garfpic, process_image_requests, generate_chat,
aod_message) aod_message, wikisum, generate_qr)
gapikey = config.GIF_TOKEN gapikey = config.GIF_TOKEN
@ -38,8 +37,11 @@ async def on_message(message):
return return
if message.content.lower().startswith("hey garfield") or isinstance(message.channel, discord.DMChannel): 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 question = message.content[12:] if message.content.lower().startswith("hey garfield") else message.content
answer = await generate_chat(question) answer = await generate_chat(question)
logger.info(f"Chat Request - User: {user}, Server: {server}, Prompt: {question}")
await message.channel.send(answer) await message.channel.send(answer)
if message.content.lower().startswith('garfpic '): if message.content.lower().startswith('garfpic '):
@ -48,17 +50,25 @@ async def on_message(message):
prompt = message.content[8:] prompt = message.content[8:]
logger.info(f"Image Request - User: {user}, Server: {server}, Prompt: {prompt}") logger.info(f"Image Request - User: {user}, Server: {server}, Prompt: {prompt}")
await message.channel.send(f"`Please wait... image generation queued: {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": if message.content.lower().startswith('garfwiki '):
# await send_gif(message, "garfield lasagna") search_term = message.content[9:]
summary = await wikisum(search_term)
await message.channel.send(summary)
# if message.content.lower() == "monday": if message.content.lower().startswith('garfqr '):
# await send_gif(message, "garfield monday") text = message.content[7:]
if len(text) > 1000:
# if message.content.lower().startswith("garfgif "): await mesage.channel.send("❌ Text too long! Maximum 1000 characters.")
# search_term = message.content[8:] else:
# await send_gif(message, search_term) 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 "): if message.content.lower().startswith("garfping "):
try: try:
@ -137,25 +147,6 @@ async def on_message(message):
await aod_message(garfbot, 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(): async def garfbot_connect():
while True: while True:
try: try:

View File

@ -4,9 +4,11 @@ from .kroger import(
kroger_token, find_store, search_product kroger_token, find_store, search_product
) )
from .garfai import( from .garfai import(
picture_time, garfpic,
process_image_requests, process_image_requests,
generate_chat generate_chat
) )
from .iputils import is_private from .iputils import is_private
from .aod import aod_message 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 txtmodel = config.TXT_MODEL
imgmodel = config.IMG_MODEL imgmodel = config.IMG_MODEL
# GarfPics
image_request_queue = asyncio.Queue() 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}) await image_request_queue.put({'message': message, 'prompt': prompt})
async def generate_image(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 openai
aiohttp aiohttp
requests requests
wikipedia
pillow
qrcode