2023-03-16 00:03:31 +01:00
import discord
import json
2023-03-16 17:33:08 +01:00
import 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-16 00:11:45 +01:00
import tempfile
2023-03-16 17:33:08 +01:00
from openai import OpenAIError
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 (
2023-05-14 15:42:16 +02:00
openai_model = " gpt-3.5-turbo "
2023-03-14 17:45:58 +01:00
)
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-16 17:35:16 +01:00
def has_kanium_role ( ) :
2023-03-15 23:18:55 +01:00
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 17:35:16 +01:00
def has_admin_role ( ) :
2023-03-15 23:18:55 +01:00
async def predicate ( ctx ) :
2023-03-16 17:33:08 +01:00
#janitor_role_id = 672156832323600396
#has_janitor_role = any(role.id == janitor_role_id for role in ctx.author.roles) # Uncomment this line
2023-03-16 00:03:31 +01:00
has_admin_permission = ctx . author . guild_permissions . administrator
2023-03-16 17:33:08 +01:00
#return has_janitor_role or has_admin_permission
return 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-05-14 16:21:39 +02:00
@commands.cooldown ( 1 , 10 , commands . BucketType . user ) # 10 second cooldown per user
2023-03-14 17:24:21 +01:00
async def reginald ( self , ctx , * , prompt = None ) :
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-18 14:47:01 +01:00
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-16 17:33:08 +01:00
except 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-05-14 16:09:08 +02:00
async def generate_response ( self , api_key , prompt ) :
2023-03-14 20:03:30 +01:00
model = await self . config . openai_model ( )
2023-05-14 16:03:36 +02:00
openai . api_key = api_key
response = openai . ChatCompletion . create (
model = model ,
max_tokens = 512 ,
n = 1 ,
stop = None ,
2023-05-14 16:21:39 +02:00
temperature = 0.7 ,
2023-05-14 16:03:36 +02:00
presence_penalty = 0.5 ,
frequency_penalty = 0.5 ,
messages = [
2023-05-14 16:29:13 +02:00
{ " role " : " system " , " content " : " You are Reginald, the butler. You are employed as a servant at an estate called The Kanium Estate. You aim to help everyone, however you can, and you always respond in a dignified and refined manner. " } ,
2023-05-14 16:03:36 +02:00
{ " role " : " user " , " content " : prompt }
]
2023-05-14 16:09:08 +02:00
)
2023-05-14 15:47:07 +02:00
return response [ ' choices ' ] [ 0 ] [ ' message ' ] [ ' content ' ] . strip ( )
2023-03-14 20:03:30 +01:00
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 17:33:08 +01:00
@has_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 ) :
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 ) )
2023-03-16 00:11:45 +01:00
with tempfile . TemporaryDirectory ( ) as temp_dir :
image_path = os . path . join ( temp_dir , " image.png " )
image . save ( image_path )
await ctx . send ( file = discord . File ( image_path ) )
2023-03-15 23:18:55 +01:00
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 ,
2023-03-16 20:30:35 +01:00
" size " : " 512x512 " ,
2023-03-15 23:52:42 +01:00
}
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
2023-03-16 00:08:02 +01:00
async def reginald_error ( self , ctx , error ) :
2023-03-15 23:18:55 +01:00
if isinstance ( error , commands . BadArgument ) :
2023-03-16 20:36:07 +01:00
await ctx . author . send ( " I ' m sorry, but I couldn ' t understand your input. Please check your message and try again. " )
2023-03-15 23:18:55 +01:00
elif isinstance ( error , commands . CheckFailure ) :
2023-03-16 20:36:07 +01:00
await ctx . author . send ( " You do not have the required role to use this command. " )
2023-03-15 23:18:55 +01:00
else :
2023-03-16 20:36:07 +01:00
await ctx . author . send ( f " An unexpected error occurred: { error } " )
2023-03-15 23:18:55 +01:00
@reginaldimagine.error
2023-03-16 00:08:02 +01:00
async def reginaldimagine_error ( self , ctx , error ) :
2023-03-15 23:18:55 +01:00
if isinstance ( error , commands . BadArgument ) :
2023-03-16 20:36:07 +01:00
await ctx . author . send ( " I ' m sorry, but I couldn ' t understand your input. Please check your message and try again. " )
2023-03-15 23:18:55 +01:00
elif isinstance ( error , commands . CheckFailure ) :
2023-03-16 20:36:07 +01:00
await ctx . author . send ( " You do not have the required role to use this command. " )
2023-03-15 23:18:55 +01:00
else :
2023-03-16 20:36:07 +01:00
await ctx . author . 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 )
2023-03-15 21:28:53 +01:00
bot . add_cog ( cog )