Compare commits
6 Commits
env-vars
...
386a7e764d
| Author | SHA1 | Date | |
|---|---|---|---|
| 386a7e764d | |||
| 4880229e26 | |||
| bfef23eb72 | |||
| 940b6b80d9 | |||
| f77940dff9 | |||
| 68696e25f0 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,7 +2,6 @@ config.py
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
garfpy/__pycache__/
|
garfpy/__pycache__/
|
||||||
*.venv*
|
*.venv*
|
||||||
.idea
|
|
||||||
*.json
|
*.json
|
||||||
*.old
|
*.old
|
||||||
*.log*
|
*.log*
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT No Attribution
|
MIT No Attribution
|
||||||
|
|
||||||
Copyright 2025 Aaron Crate
|
Copyright 2026 Aaron Crate
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
software and associated documentation files (the "Software"), to deal in the Software
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
|||||||
@@ -2,14 +2,6 @@ services:
|
|||||||
garfbot:
|
garfbot:
|
||||||
image: git.crate.zip/crate/garfbot:latest
|
image: git.crate.zip/crate/garfbot:latest
|
||||||
container_name: garfbot
|
container_name: garfbot
|
||||||
environment:
|
|
||||||
- TXT_MODEL=${TXT_MODEL}
|
|
||||||
- IMG_MODEL=${IMG_MODEL}
|
|
||||||
- GARFBOT_TOKEN=${GARFBOT_TOKEN}
|
|
||||||
- OPENAI_TOKEN=${OPENAI_TOKEN}
|
|
||||||
- WEATHER_TOKEN=${WEATHER_TOKEN}
|
|
||||||
- KROGER_ID=${KROGER_ID}
|
|
||||||
- KROGER_SECRET=${KROGER_SECRET}
|
|
||||||
restart: always
|
restart: always
|
||||||
volumes:
|
volumes:
|
||||||
- /home/crate/garfbot:/usr/src/app
|
- /home/crate/garfbot:/usr/src/app
|
||||||
|
|||||||
59
garfmain.py
59
garfmain.py
@@ -1,7 +1,9 @@
|
|||||||
import os
|
import re
|
||||||
|
import config
|
||||||
import asyncio
|
import asyncio
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
from urllib.parse import urlparse, parse_qs
|
||||||
|
|
||||||
from garfpy import (
|
from garfpy import (
|
||||||
help,
|
help,
|
||||||
@@ -16,9 +18,10 @@ from garfpy import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
garfkey = os.getenv("GARFBOT_TOKEN")
|
# gapikey = config.GIF_TOKEN
|
||||||
txtmodel = os.getenv("TXT_MODEL")
|
garfkey = config.GARFBOT_TOKEN
|
||||||
imgmodel = os.getenv("IMG_MODEL")
|
txtmodel = config.TXT_MODEL
|
||||||
|
imgmodel = config.IMG_MODEL
|
||||||
|
|
||||||
intents = discord.Intents.default()
|
intents = discord.Intents.default()
|
||||||
intents.members = True
|
intents.members = True
|
||||||
@@ -39,6 +42,36 @@ kroger = Kroger()
|
|||||||
weather = WeatherAPI()
|
weather = WeatherAPI()
|
||||||
|
|
||||||
|
|
||||||
|
URL_PATTERNS = [
|
||||||
|
r'https?://(?:www\.)?youtube\.com/watch\?[^\s]*',
|
||||||
|
r'https?://youtu\.be/[^\s]*',
|
||||||
|
r'https?://(?:open\.)?spotify\.com/[^\s]*',
|
||||||
|
]
|
||||||
|
|
||||||
|
def clean_url(url):
|
||||||
|
try:
|
||||||
|
parsed = urlparse(url)
|
||||||
|
|
||||||
|
if 'youtube.com' in parsed.hostname:
|
||||||
|
params = parse_qs(parsed.query)
|
||||||
|
video_id = params.get('v', [None])[0]
|
||||||
|
if not video_id:
|
||||||
|
return None
|
||||||
|
# timestamp = params.get('t', [None])[0]
|
||||||
|
# if timestamp:
|
||||||
|
# return f"https://www.youtube.com/watch?v={video_id}&t={timestamp}"
|
||||||
|
return f"https://www.youtube.com/watch?v={video_id}"
|
||||||
|
|
||||||
|
if 'youtu.be' in parsed.hostname:
|
||||||
|
return f"https://youtu.be{parsed.path}"
|
||||||
|
|
||||||
|
if 'spotify.com' in parsed.hostname:
|
||||||
|
return f"https://open.spotify.com{parsed.path}"
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@garfbot.event
|
@garfbot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
try:
|
try:
|
||||||
@@ -117,6 +150,9 @@ async def garfbot_weather(ctx, *, location):
|
|||||||
|
|
||||||
@garfbot.command(name="chat")
|
@garfbot.command(name="chat")
|
||||||
async def garfchat(ctx, *, prompt):
|
async def garfchat(ctx, *, prompt):
|
||||||
|
if "is this true" in prompt.lower():
|
||||||
|
messages = [msg async for msg in ctx.channel.history(limit=2)]
|
||||||
|
prompt = messages[1].content
|
||||||
answer = await garfield.generate_chat(prompt)
|
answer = await garfield.generate_chat(prompt)
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Chat Request - User: {ctx.author.name}, Server: {ctx.guild.name}, Prompt: {prompt}"
|
f"Chat Request - User: {ctx.author.name}, Server: {ctx.guild.name}, Prompt: {prompt}"
|
||||||
@@ -147,6 +183,19 @@ async def on_message(message):
|
|||||||
content = message.content.strip()
|
content = message.content.strip()
|
||||||
lower = content.lower()
|
lower = content.lower()
|
||||||
|
|
||||||
|
# # Remove tracking stuff from youtube and spotify links
|
||||||
|
# cleaned_urls = []
|
||||||
|
|
||||||
|
# for pattern in URL_PATTERNS:
|
||||||
|
# for match in re.finditer(pattern, message.content):
|
||||||
|
# cleaned = clean_url(match.group(0))
|
||||||
|
# if cleaned and cleaned != match.group(0):
|
||||||
|
# cleaned_urls.append(cleaned)
|
||||||
|
|
||||||
|
# if cleaned_urls:
|
||||||
|
# links = '\n'.join(cleaned_urls)
|
||||||
|
# await message.reply(f"🔗 Cleaned link{'s' if len(cleaned_urls) > 1 else ''}:\n{links}")
|
||||||
|
|
||||||
# Chats & pics
|
# Chats & pics
|
||||||
if lower.startswith("hey garfield") or isinstance(
|
if lower.startswith("hey garfield") or isinstance(
|
||||||
message.channel, discord.DMChannel
|
message.channel, discord.DMChannel
|
||||||
@@ -180,7 +229,7 @@ async def on_message(message):
|
|||||||
async def garfbot_connect():
|
async def garfbot_connect():
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
await garfbot.start(garfkey) # type: ignore
|
await garfbot.start(garfkey)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
e = str(e)
|
e = str(e)
|
||||||
logger.error(f"Garfbot couldn't connect! {e}")
|
logger.error(f"Garfbot couldn't connect! {e}")
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import json
|
import json
|
||||||
import discord
|
import discord
|
||||||
from garfpy import logger
|
from garfpy import logger
|
||||||
@@ -121,3 +122,17 @@ async def aod_message(garfbot, message):
|
|||||||
for field, values in zip(table_fields, table_columns):
|
for field, values in zip(table_fields, table_columns):
|
||||||
stats_embed.add_field(name=field, value="\n".join(values), inline=True)
|
stats_embed.add_field(name=field, value="\n".join(values), inline=True)
|
||||||
await message.channel.send(embed=stats_embed)
|
await message.channel.send(embed=stats_embed)
|
||||||
|
|
||||||
|
# Boy You Said It
|
||||||
|
words = re.findall(r"[a-zA-Z']+", message.content.lower())
|
||||||
|
stops = {"a", "an", "the", "and", "or", "but", "is", "it", "in", "on", "at", "to", "of"}
|
||||||
|
words = [w for w in words if w not in stops]
|
||||||
|
|
||||||
|
if words:
|
||||||
|
firsts = [w[0] for w in words]
|
||||||
|
commons = max(set(firsts), key=firsts.count)
|
||||||
|
count = firsts.count(commons)
|
||||||
|
|
||||||
|
if count >= 3 or (len(words) >= 2 and count / len(words) >= 0.75):
|
||||||
|
await message.channel.send("Boy, you said it!")
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import io
|
import io
|
||||||
import os
|
|
||||||
import openai
|
import openai
|
||||||
|
import config
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import asyncio
|
import asyncio
|
||||||
import discord
|
import discord
|
||||||
@@ -11,9 +11,9 @@ from garfpy import logger
|
|||||||
|
|
||||||
class GarfAI:
|
class GarfAI:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.openaikey = os.getenv('OPENAI_TOKEN')
|
self.openaikey = config.OPENAI_TOKEN
|
||||||
self.txtmodel = os.getenv('TXT_MODEL')
|
self.txtmodel = config.TXT_MODEL
|
||||||
self.imgmodel = os.getenv('IMG_MODEL')
|
self.imgmodel = config.IMG_MODEL
|
||||||
self.image_request_queue = asyncio.Queue()
|
self.image_request_queue = asyncio.Queue()
|
||||||
|
|
||||||
async def garfpic(self, ctx, prompt):
|
async def garfpic(self, ctx, prompt):
|
||||||
@@ -82,7 +82,7 @@ class GarfAI:
|
|||||||
try:
|
try:
|
||||||
client = AsyncOpenAI(api_key=self.openaikey)
|
client = AsyncOpenAI(api_key=self.openaikey)
|
||||||
response = await client.chat.completions.create(
|
response = await client.chat.completions.create(
|
||||||
model=self.txtmodel, # type: ignore
|
model=self.txtmodel,
|
||||||
messages=[
|
messages=[
|
||||||
{
|
{
|
||||||
"role": "system",
|
"role": "system",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import os
|
import config
|
||||||
import requests
|
import requests
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from garfpy import logger
|
from garfpy import logger
|
||||||
@@ -6,8 +6,8 @@ from garfpy import logger
|
|||||||
|
|
||||||
class Kroger:
|
class Kroger:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.client_id = os.getenv('CLIENT_ID')
|
self.client_id = config.CLIENT_ID
|
||||||
self.client_secret = os.getenv('CLIENT_SECRET')
|
self.client_secret = config.CLIENT_SECRET
|
||||||
self.auth = b64encode(
|
self.auth = b64encode(
|
||||||
f"{self.client_id}:{self.client_secret}".encode()
|
f"{self.client_id}:{self.client_secret}".encode()
|
||||||
).decode()
|
).decode()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
import config
|
||||||
import discord
|
import discord
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from garfpy import logger
|
from garfpy import logger
|
||||||
@@ -7,7 +7,7 @@ from garfpy import logger
|
|||||||
|
|
||||||
class WeatherAPI:
|
class WeatherAPI:
|
||||||
def __init__(self, api_key=None):
|
def __init__(self, api_key=None):
|
||||||
self.api_key = api_key or os.getenv("WEATHER_TOKEN")
|
self.api_key = api_key or config.WEATHER_TOKEN
|
||||||
self.base_url = "https://api.openweathermap.org/data/2.5/weather"
|
self.base_url = "https://api.openweathermap.org/data/2.5/weather"
|
||||||
|
|
||||||
def parse_location(self, location):
|
def parse_location(self, location):
|
||||||
@@ -126,7 +126,7 @@ class WeatherAPI:
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
return await response.json()
|
return await response.json()
|
||||||
except aiohttp.ClientError as e:
|
except aiohttp.ClientError as e:
|
||||||
logger.error(f"Error fetching weather data for '{location}'")
|
logger.error(f"Error fetching weather data for '{location}' - {e}")
|
||||||
await ctx.send(f"`Error fetching weather data for '{location}'`")
|
await ctx.send(f"`Error fetching weather data for '{location}'`")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user