From 602db1bbf2d411f87d728518d76f0778ec38bd1d Mon Sep 17 00:00:00 2001 From: Aaron Crate Date: Thu, 18 Jun 2026 15:01:30 -0500 Subject: [PATCH 1/2] re-add jonbot and do some linting --- .gitea/workflows/deploy.yaml | 2 +- docker-compose.yml | 15 ++++++++- garfmain.py | 1 - garfpy/__init__.py | 19 ++++++------ garfpy/aod.py | 1 - jonbot/Dockerfile | 30 ++++++++++++++++++ {etc => jonbot}/jonbot.py | 59 +++++++++++++++++++++++++++++++++++- 7 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 jonbot/Dockerfile rename {etc => jonbot}/jonbot.py (52%) diff --git a/.gitea/workflows/deploy.yaml b/.gitea/workflows/deploy.yaml index d0efbe6..e7e9b6c 100644 --- a/.gitea/workflows/deploy.yaml +++ b/.gitea/workflows/deploy.yaml @@ -23,7 +23,7 @@ jobs: docker compose down fi docker build -t git.crate.zip/crate/garfbot:latest . - docker compose up -d + docker compose up -d --build else docker restart garfbot fi diff --git a/docker-compose.yml b/docker-compose.yml index 4066fb3..f9a3125 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,19 @@ services: garfbot: image: git.crate.zip/crate/garfbot:latest container_name: garfbot + build: + context: . + dockerfile: Dockerfile restart: always volumes: - - /home/crate/garfbot:/usr/src/app + - ../garfbot:/usr/src/app + + jonbot: + image: git.crate.zip/crate/jonbot:latest + container_name: jonbot + build: + context: ./jonbot + dockerfile: Dockerfile + restart: always + volumes: + - ../garfbot/jonbot:/usr/src/app diff --git a/garfmain.py b/garfmain.py index fb4b04a..6e1e5d5 100644 --- a/garfmain.py +++ b/garfmain.py @@ -1,4 +1,3 @@ -import re import config import asyncio import discord diff --git a/garfpy/__init__.py b/garfpy/__init__.py index 3282bdb..7019cd3 100644 --- a/garfpy/__init__.py +++ b/garfpy/__init__.py @@ -1,12 +1,11 @@ # garfpy/__init__.py -from .log import logger -from .help import help -from .kroger import Kroger -from .kroger import Kroger -from .garfai import GarfAI -from .respond import GarfbotRespond -from .aod import aod_message -from .qr import generate_qr -from .iputils import IPUtils -from .weather import WeatherAPI +from .log import logger as logger +from .help import help as help +from .kroger import Kroger as Kroger +from .garfai import GarfAI as GarfAI +from .respond import GarfbotRespond as GarfbotRespond +from .aod import aod_message as aod_message +from .qr import generate_qr as generate_qr +from .iputils import IPUtils as IPUtils +from .weather import WeatherAPI as WeatherAPI diff --git a/garfpy/aod.py b/garfpy/aod.py index 73e9d3f..9747e07 100644 --- a/garfpy/aod.py +++ b/garfpy/aod.py @@ -1,5 +1,4 @@ import os -import re import json import discord from garfpy import logger diff --git a/jonbot/Dockerfile b/jonbot/Dockerfile new file mode 100644 index 0000000..745e2f2 --- /dev/null +++ b/jonbot/Dockerfile @@ -0,0 +1,30 @@ +FROM python:alpine +WORKDIR /usr/src/app + +RUN apk update && \ + apk add --no-cache \ + iputils \ + bind-tools \ + nmap \ + gcc \ + musl-dev \ + jpeg-dev \ + zlib-dev \ + freetype-dev \ + lcms2-dev \ + openjpeg-dev \ + tiff-dev \ + tk-dev \ + tcl-dev + +RUN pip3 install --no-cache-dir \ + config \ + discord \ + openai \ + aiohttp \ + requests \ + wikipedia \ + pillow \ + qrcode + +CMD [ "python", "jonbot.py" ] diff --git a/etc/jonbot.py b/jonbot/jonbot.py similarity index 52% rename from etc/jonbot.py rename to jonbot/jonbot.py index dbe8101..6fe3743 100644 --- a/etc/jonbot.py +++ b/jonbot/jonbot.py @@ -2,7 +2,33 @@ import config import openai import discord import asyncio -import os +import logging +from logging.handlers import TimedRotatingFileHandler +from openai import AsyncOpenAI + + +logger = logging.getLogger("garflog") +logger.setLevel(logging.INFO) +formatter = logging.Formatter( + "%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S" +) +file_handler = TimedRotatingFileHandler( + "jonbot.log", + when="midnight", + interval=1, + backupCount=7, + delay=True, +) +file_handler.setFormatter(formatter) +console_handler = logging.StreamHandler() +console_handler.setFormatter(formatter) +logger.addHandler(file_handler) +logger.addHandler(console_handler) + +if not logger.hasHandlers(): + logger.addHandler(file_handler) + logger.addHandler(console_handler) + openai.api_key = config.OPENAI_TOKEN jonkey = config.JONBOT_TOKEN @@ -13,6 +39,7 @@ intents.messages = True intents.message_content = True client = discord.Client(intents=intents) + @client.event async def on_ready(): print(f"Logged in as {client.user.name} running {model}.", flush=True) @@ -40,6 +67,36 @@ async def on_message(message): e = str(e) await message.channel.send(f"`JonBot Error: {e}`") +oai = AsyncOpenAI( + api_key=config.OPENAI_TOKEN, + base_url=config.BASE_URL, +) + +async def generate_chat(self, question: str) -> str: + try: + response = await oai.chat.completions.create( + model=self.txtmodel, + messages=[ + {"role": "system", "content": self.sysprompt}, + {"role": "user", "content": question}, + ], + max_tokens=400, + temperature=1.2, + ) + answer = response.choices[0].message.content + return answer.replace("an AI language model", "a cartoon animal") + except openai.BadRequestError as e: + logger.error(e) + return f"`JonBot Error: {e}`" + except openai.APIError as e: + logger.error(e) + return "`JonBot Error: Odie`" + except Exception as e: + logger.error(e) + return "`JonBot Error: Liz`" + + + async def jonbot_connect(): while True: try: From e94173b3dad28fae1748b4ecc47641a5b06faf2e Mon Sep 17 00:00:00 2001 From: Aaron Crate Date: Thu, 18 Jun 2026 15:22:20 -0500 Subject: [PATCH 2/2] fix jon chats and merge main into branch --- jonbot/jonbot.py | 50 ++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/jonbot/jonbot.py b/jonbot/jonbot.py index 6fe3743..912e380 100644 --- a/jonbot/jonbot.py +++ b/jonbot/jonbot.py @@ -1,10 +1,11 @@ import config import openai +from openai import AsyncOpenAI import discord +from discord.ext import commands import asyncio import logging from logging.handlers import TimedRotatingFileHandler -from openai import AsyncOpenAI logger = logging.getLogger("garflog") @@ -37,35 +38,42 @@ model = config.TXT_MODEL intents = discord.Intents.default() intents.messages = True intents.message_content = True -client = discord.Client(intents=intents) +client = commands.Bot( + command_prefix=["Jonbot ", "jonbot ", "Jon", "jon"], + case_insensitive=True, + intents=intents, +) @client.event async def on_ready(): print(f"Logged in as {client.user.name} running {model}.", flush=True) +@client.command(name="chat") +async def jonchat(ctx, *, prompt): + if "is this true" in prompt.lower(): + messages = [msg async for msg in ctx.channel.history(limit=2)] + prompt = messages[1].content + prompt = f"Is this true: {prompt}" + answer = await generate_chat(prompt) + logger.info( + f"Chat Request - User: {ctx.author.name}, Server: {ctx.guild.name}, Prompt: {prompt}" + ) + await ctx.reply(answer) + @client.event async def on_message(message): if message.author == client.user: return - if message.content.lower().startswith("hey jon") or isinstance(message.channel, discord.DMChannel): - question = message.content[7:] if message.content.lower().startswith("hey jon") else message.content - try: - response = openai.ChatCompletion.create( - model=model, - messages=[ - {"role": "system", "content": "Pretend you are friendly Jon Arbuckle."}, - {"role": "user", "content": f"{question}"} - ], - max_tokens=400 - ) - answer = response['choices'][0]['message']['content'] - answer = answer.replace("AI language model", "American citizen") - answer = answer.replace("language model AI", "citizen of the United States") - await message.channel.send(answer) - except Exception as e: - e = str(e) - await message.channel.send(f"`JonBot Error: {e}`") + + content = message.content.strip() + lower = content.lower() + if lower.startswith("hey jon") or isinstance( + message.channel, discord.DMChannel + ): + ctx = await client.get_context(message) + await jonchat(ctx, prompt=content) + oai = AsyncOpenAI( api_key=config.OPENAI_TOKEN, @@ -84,7 +92,7 @@ async def generate_chat(self, question: str) -> str: temperature=1.2, ) answer = response.choices[0].message.content - return answer.replace("an AI language model", "a cartoon animal") + return answer except openai.BadRequestError as e: logger.error(e) return f"`JonBot Error: {e}`"