KaniumCogs/reginaldCog/reginald.py

187 lines
7.1 KiB
Python
Raw Normal View History

2023-03-16 00:03:31 +01:00
import discord
import json
import openai_async as openai
2023-03-14 18:49:44 +01:00
import os
2023-03-14 19:15:46 +01:00
import random
2023-03-15 23:18:55 +01:00
import requests
import base64
import aiohttp
from io import BytesIO
from PIL import Image
2023-03-14 17:45:58 +01:00
from redbot.core import Config, commands
2023-03-14 17:24:21 +01:00
2023-03-16 00:03:31 +01:00
2023-03-14 17:24:21 +01:00
class ReginaldCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
2023-03-14 17:59:57 +01:00
self.config = Config.get_conf(self, identifier=71717171171717)
2023-03-14 17:45:58 +01:00
self.config.register_global(
openai_model="text-davinci-002"
)
2023-03-14 18:58:49 +01:00
self.config.register_guild(
openai_api_key=None
)
2023-03-14 17:24:21 +01:00
2023-03-15 23:18:55 +01:00
def has_kanium_role():
async def predicate(ctx):
kanium_role_id = 280260875678515200
return any(role.id == kanium_role_id for role in ctx.author.roles)
return commands.check(predicate)
2023-03-16 00:03:31 +01:00
def has_janitor_or_admin_role():
2023-03-15 23:18:55 +01:00
async def predicate(ctx):
janitor_role_id = 672156832323600396
2023-03-16 00:03:31 +01:00
has_janitor_role = any(role.id == janitor_role_id for role in ctx.author.roles)
has_admin_permission = ctx.author.guild_permissions.administrator
return has_janitor_role or has_admin_permission
2023-03-15 23:18:55 +01:00
return commands.check(predicate)
2023-03-14 17:54:14 +01:00
@commands.guild_only()
@commands.has_permissions(manage_guild=True)
2023-03-14 20:03:30 +01:00
@commands.command(help="Set the OpenAI API key")
2023-03-14 18:58:49 +01:00
async def setreginaldcogapi(self, ctx, api_key):
await self.config.guild(ctx.guild).openai_api_key.set(api_key)
await ctx.send("OpenAI API key set successfully.")
@commands.guild_only()
2023-03-15 23:18:55 +01:00
@has_kanium_role()
2023-03-14 20:03:30 +01:00
@commands.command(help="Ask Reginald a question")
2023-03-15 21:50:17 +01:00
@commands.cooldown(1, 60, commands.BucketType.user) # 1-minute cooldown per user
2023-03-14 17:24:21 +01:00
async def reginald(self, ctx, *, prompt=None):
ignored_user_id = 138125632876838912
if ctx.author.id == ignored_user_id:
return
2023-03-14 19:15:46 +01:00
greetings = [
"Greetings! How may I be of assistance to you?",
"Yes? How may I help?",
"Good day! How can I help you?",
"You rang? What can I do for you?",
]
2023-03-14 17:24:21 +01:00
if prompt is None:
2023-03-14 19:15:46 +01:00
await ctx.send(random.choice(greetings))
return
2023-03-14 20:03:30 +01:00
api_key = await self.config.guild(ctx.guild).openai_api_key()
if api_key is None:
await ctx.author.send('OpenAI API key not set. Please use the "!setreginaldcogapi" command to set the key.')
return
2023-03-14 17:54:14 +01:00
try:
2023-03-14 20:03:30 +01:00
response_text = await self.generate_response(api_key, prompt)
2023-03-15 21:50:17 +01:00
for chunk in self.split_response(response_text, 2000):
await ctx.send(chunk)
2023-03-14 17:54:14 +01:00
except openai.error.OpenAIError as e:
2023-03-14 19:15:46 +01:00
await ctx.send(f"I apologize, but I am unable to generate a response at this time. Error message: {str(e)}")
2023-03-15 21:50:17 +01:00
except commands.CommandOnCooldown as e:
remaining_seconds = int(e.retry_after)
await ctx.author.send(f'Please wait {remaining_seconds} seconds before using the "reginald" command again.')
2023-03-14 17:24:21 +01:00
2023-03-14 20:03:30 +01:00
async def generate_response(self, api_key, prompt):
model = await self.config.openai_model()
openai.api_key = api_key
max_tokens = 1000
2023-03-15 21:50:17 +01:00
temperature = 0.5
2023-03-16 00:03:31 +01:00
response = await openai.Completion.create(
2023-03-14 20:03:30 +01:00
model=model,
prompt=prompt,
max_tokens=max_tokens,
n=1,
stop=None,
temperature=temperature,
2023-03-16 00:03:31 +01:00
presence_penalty=0.5,
frequency_penalty=0.5,
2023-03-14 20:03:30 +01:00
best_of=3
)
2023-03-16 00:03:31 +01:00
2023-03-14 20:03:30 +01:00
return response.choices[0].text.strip()
2023-03-15 21:50:17 +01:00
@staticmethod
def split_response(response_text, max_chars):
chunks = []
while len(response_text) > max_chars:
split_index = response_text[:max_chars].rfind(' ')
chunk = response_text[:split_index]
chunks.append(chunk)
response_text = response_text[split_index:].strip()
chunks.append(response_text)
return chunks
2023-03-15 23:18:55 +01:00
@commands.guild_only()
2023-03-16 00:03:31 +01:00
@has_janitor_or_admin_role()
2023-03-15 23:18:55 +01:00
@commands.command(help="Ask Reginald to generate an image based on a prompt")
@commands.cooldown(1, 300, commands.BucketType.user) # 5-minute cooldown per user
async def reginaldimagine(self, ctx, *, prompt=None):
ignored_user_id = 138125632876838912
if ctx.author.id == ignored_user_id:
return
if prompt is None:
await ctx.author.send("Please provide a prompt for the image.")
return
api_key = await self.config.guild(ctx.guild).openai_api_key()
if api_key is None:
await ctx.author.send('OpenAI API key not set. Please use the "!setreginaldcogapi" command to set the key.')
return
try:
image_url = await self.generate_image(api_key, prompt)
if image_url:
async with ctx.typing():
async with aiohttp.ClientSession() as session:
async with session.get(image_url) as resp:
image_data = await resp.read()
image = Image.open(BytesIO(image_data))
image.save("image.png")
await ctx.send(file=discord.File("image.png"))
os.remove("image.png")
else:
await ctx.author.send("I apologize, but I am unable to generate an image based on the provided prompt.")
except Exception as e:
await ctx.author.send(f"I apologize, but I am unable to generate an image at this time. Error message: {str(e)}")
async def generate_image(self, api_key, prompt):
2023-03-15 23:52:42 +01:00
url = "https://api.openai.com/v1/images/generations"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
}
data = {
"prompt": prompt,
"n": 1,
"size": "1024x1024",
}
async with aiohttp.ClientSession() as session:
async with session.post(url, headers=headers, data=json.dumps(data)) as resp:
response = await resp.json()
if response and "data" in response and len(response["data"]) > 0:
return response["data"][0]["url"]
2023-03-15 23:18:55 +01:00
return None
@reginald.error
async def reginald_error(ctx, error):
if isinstance(error, commands.BadArgument):
await ctx.send("I'm sorry, but I couldn't understand your input. Please check your message and try again.")
elif isinstance(error, commands.CheckFailure):
await ctx.send("You do not have the required role to use this command.")
else:
await ctx.send(f"An unexpected error occurred: {error}")
@reginaldimagine.error
async def reginaldimagine_error(ctx, error):
if isinstance(error, commands.BadArgument):
await ctx.send("I'm sorry, but I couldn't understand your input. Please check your message and try again.")
elif isinstance(error, commands.CheckFailure):
await ctx.send("You do not have the required role to use this command.")
else:
await ctx.send(f"An unexpected error occurred: {error}")
2023-03-15 21:50:17 +01:00
2023-03-14 17:24:21 +01:00
def setup(bot):
2023-03-14 17:40:21 +01:00
cog = ReginaldCog(bot)
bot.add_cog(cog)