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/etc/jonbot.py b/etc/jonbot.py deleted file mode 100644 index dbe8101..0000000 --- a/etc/jonbot.py +++ /dev/null @@ -1,53 +0,0 @@ -import config -import openai -import discord -import asyncio -import os - -openai.api_key = config.OPENAI_TOKEN -jonkey = config.JONBOT_TOKEN -model = config.TXT_MODEL - -intents = discord.Intents.default() -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) - -@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}`") - -async def jonbot_connect(): - while True: - try: - await client.start(jonkey) - except Exception as e: - e = str(e) - logger.error(f"Jonbot couldn't connect! {e}") - await asyncio.sleep(60) - -if __name__ == "__main__": - asyncio.run(jonbot_connect()) 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/jonbot/jonbot.py b/jonbot/jonbot.py new file mode 100644 index 0000000..912e380 --- /dev/null +++ b/jonbot/jonbot.py @@ -0,0 +1,118 @@ +import config +import openai +from openai import AsyncOpenAI +import discord +from discord.ext import commands +import asyncio +import logging +from logging.handlers import TimedRotatingFileHandler + + +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 +model = config.TXT_MODEL + +intents = discord.Intents.default() +intents.messages = True +intents.message_content = True + +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 + + 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, + 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 + 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: + await client.start(jonkey) + except Exception as e: + e = str(e) + logger.error(f"Jonbot couldn't connect! {e}") + await asyncio.sleep(60) + +if __name__ == "__main__": + asyncio.run(jonbot_connect())