From 736945b5e79bd50ab46b224d801403b2ed855302 Mon Sep 17 00:00:00 2001 From: Rene Teigen Date: Sat, 31 Dec 2022 11:39:08 +0000 Subject: [PATCH 01/12] Add gitignore --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e979e2b --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +#cache folders +__pycache__ +/cogs/__pycache__ +/models/__pycache__ +#user files +.env +bot.pid +usage.txt +/dalleimages \ No newline at end of file From 3ddb1056c348b6d97c7191a9eac807535b3fdedb Mon Sep 17 00:00:00 2001 From: Rene Teigen Date: Sat, 31 Dec 2022 14:13:05 +0000 Subject: [PATCH 02/12] Change way that roles are checked and some formatting --- cogs/draw_image_generation.py | 14 ++++---- cogs/gpt_3_commands_and_converser.py | 54 ++++++++++++++++------------ cogs/image_prompt_optimizer.py | 5 ++- gpt3discord.py | 10 ++++++ models/check_model.py | 22 ++++++++++++ 5 files changed, 72 insertions(+), 33 deletions(-) create mode 100644 models/check_model.py diff --git a/cogs/draw_image_generation.py b/cogs/draw_image_generation.py index 7d39c62..f23a206 100644 --- a/cogs/draw_image_generation.py +++ b/cogs/draw_image_generation.py @@ -12,6 +12,7 @@ from discord.ext import commands # We don't use the converser cog here because we want to be able to redo for the last images and text prompts at the same time from models.env_service_model import EnvService from models.user_model import RedoUser +from models.check_model import Check redo_users = {} users_to_interactions = {} @@ -131,7 +132,10 @@ class DrawDallEService(commands.Cog, name="DrawDallEService"): ) @discord.slash_command( - name="draw", description="Draw an image from a prompt", guild_ids=ALLOWED_GUILDS + name="draw", + description="Draw an image from a prompt", + guild_ids=ALLOWED_GUILDS, + checks=[Check.check_valid_roles()], ) @discord.option(name="prompt", description="The prompt to draw from", required=True) async def draw(self, ctx: discord.ApplicationContext, prompt: str): @@ -142,10 +146,6 @@ class DrawDallEService(commands.Cog, name="DrawDallEService"): if user == self.bot.user: return - # Only allow the bot to be used by people who have the role "Admin" or "GPT" - if not await self.converser_cog.check_valid_roles(ctx.user, ctx): - return - try: asyncio.ensure_future(self.encapsulated_send(user.id, prompt, ctx)) @@ -183,14 +183,12 @@ class DrawDallEService(commands.Cog, name="DrawDallEService"): name="clear-local", description="Clear the local dalleimages folder on system.", guild_ids=ALLOWED_GUILDS, + checks=[Check.check_valid_roles()], ) @discord.guild_only() async def clear_local(self, ctx): await ctx.defer() - if not await self.converser_cog.check_valid_roles(ctx.user, ctx): - return - # Delete all the local images in the images folder. image_path = self.model.IMAGE_SAVE_PATH for dirpath, dirnames, filenames in os.walk(image_path): diff --git a/cogs/gpt_3_commands_and_converser.py b/cogs/gpt_3_commands_and_converser.py index af1e40f..05d5248 100644 --- a/cogs/gpt_3_commands_and_converser.py +++ b/cogs/gpt_3_commands_and_converser.py @@ -11,6 +11,7 @@ from models.deletion_service_model import Deletion from models.env_service_model import EnvService from models.message_model import Message from models.user_model import User, RedoUser +from models.check_model import Check from collections import defaultdict @@ -37,7 +38,6 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): self._last_member_ = None self.conversating_users = {} self.DAVINCI_ROLES = ["admin", "Admin", "GPT", "gpt"] - self.ALLOWED_ROLES = EnvService.get_allowed_roles() self.END_PROMPTS = [ "end", "end conversation", @@ -83,12 +83,6 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): self.message_queue = message_queue self.conversation_threads = {} - async def check_valid_roles(self, user, ctx): - if not any(role.name in self.ALLOWED_ROLES for role in user.roles): - await ctx.respond("You don't have permission to use this.") - return False - return True - @commands.Cog.listener() async def on_member_remove(self, member): pass @@ -101,7 +95,9 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): print(f"The debug channel was acquired") @discord.slash_command( - name="set-usage", description="Set the current OpenAI usage (in dollars)" + name="set-usage", + description="Set the current OpenAI usage (in dollars)", + checks=[Check.check_valid_roles()], ) @discord.option( name="usage_amount", @@ -111,9 +107,6 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): async def set_usage(self, ctx, usage_amount: float): await ctx.defer() - if not await self.check_valid_roles(ctx.user, ctx): - return - # Attempt to convert the input usage value into a float try: usage = float(usage_amount) @@ -126,12 +119,10 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): @discord.slash_command( name="delete-conversation-threads", description="Delete all conversation threads across the bot servers.", + checks=[Check.check_valid_roles()], ) async def delete_all_conversation_threads(self, ctx): await ctx.defer() - # If the user has ADMIN_ROLES - if not await self.check_valid_roles(ctx.user, ctx): - return for guild in self.bot.guilds: for thread in guild.threads: @@ -626,7 +617,10 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): return @discord.slash_command( - name="g", description="Ask GPT3 something!", guild_ids=ALLOWED_GUILDS + name="g", + description="Ask GPT3 something!", + guild_ids=ALLOWED_GUILDS, + checks=[Check.check_valid_roles()], ) @discord.option( name="prompt", description="The prompt to send to GPT3", required=True @@ -638,9 +632,6 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): user = ctx.user prompt = prompt.strip() - if not await self.check_valid_roles(user, ctx): - return - # CONVERSE Checks here TODO # Send the request to the model # If conversing, the prompt to send is the history, otherwise, it's just the prompt @@ -657,14 +648,12 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): name="chat-gpt", description="Have a conversation with GPT3", guild_ids=ALLOWED_GUILDS, + checks=[Check.check_valid_roles()], ) @discord.guild_only() async def chat_gpt(self, ctx: discord.ApplicationContext): await ctx.defer() - if not await self.check_valid_roles(ctx.user, ctx): - return - user = ctx.user if user.id in self.conversating_users: @@ -730,7 +719,28 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): guild_ids=ALLOWED_GUILDS, ) @discord.option( - name="parameter", description="The setting to change", required=False + name="parameter", + description="The setting to change", + required=False, + choices=[ + "mode", + "temp", + "top_p", + "max_tokens", + "presence_penalty", + "frequency_penalty", + "best_of", + "prompt_min_length", + "max_conversation_length", + "model", + "low_usage_mode", + "image_size", + "num_images", + "summarize_conversations", + "summarize_threshold", + "IMAGE_SAVE_PATH", + "openai_key", + ], ) @discord.option( name="value", description="The value to set the setting to", required=False diff --git a/cogs/image_prompt_optimizer.py b/cogs/image_prompt_optimizer.py index 144b417..88d2e92 100644 --- a/cogs/image_prompt_optimizer.py +++ b/cogs/image_prompt_optimizer.py @@ -6,6 +6,7 @@ from discord.ext import commands from models.env_service_model import EnvService from models.user_model import RedoUser +from models.check_model import Check ALLOWED_GUILDS = EnvService.get_allowed_guilds() @@ -50,6 +51,7 @@ class ImgPromptOptimizer(commands.Cog, name="ImgPromptOptimizer"): name="imgoptimize", description="Optimize a text prompt for DALL-E/MJ/SD image generation.", guild_ids=ALLOWED_GUILDS, + checks=[Check.check_valid_roles()], ) @discord.option( name="prompt", description="The text prompt to optimize.", required=True @@ -58,9 +60,6 @@ class ImgPromptOptimizer(commands.Cog, name="ImgPromptOptimizer"): async def imgoptimize(self, ctx: discord.ApplicationContext, prompt: str): await ctx.defer() - if not await self.converser_cog.check_valid_roles(ctx.user, ctx): - return - user = ctx.user final_prompt = self.OPTIMIZER_PRETEXT diff --git a/gpt3discord.py b/gpt3discord.py index 0d4c2e6..8ebf9f7 100644 --- a/gpt3discord.py +++ b/gpt3discord.py @@ -46,6 +46,16 @@ async def on_ready(): # I can make self optional by print("We have logged in as {0.user}".format(bot)) +@bot.event +async def on_application_command_error( + ctx: discord.ApplicationContext, error: discord.DiscordException +): + if isinstance(error, discord.CheckFailure): + pass + else: + raise error + + async def main(): data_path = Path(os.environ.get("DATA_DIR", os.getcwd())) debug_guild = int(os.getenv("DEBUG_GUILD")) diff --git a/models/check_model.py b/models/check_model.py new file mode 100644 index 0000000..c184404 --- /dev/null +++ b/models/check_model.py @@ -0,0 +1,22 @@ +import discord +from models.env_service_model import EnvService +from typing import Callable + + +ALLOWED_ROLES = EnvService.get_allowed_roles() + + +class Check: + def check_valid_roles() -> Callable: + async def inner(ctx: discord.ApplicationContext): + if not any(role.name in ALLOWED_ROLES for role in ctx.user.roles): + await ctx.defer(ephemeral=True) + await ctx.send_followup( + "You don't have permission to use this.", + ephemeral=True, + delete_after=10, + ) + return False + return True + + return inner From 39c193adfacea8451f73da4ac51ce4c1288e968b Mon Sep 17 00:00:00 2001 From: Hikari Haru Date: Sat, 31 Dec 2022 21:02:36 +0100 Subject: [PATCH 03/12] Update gpt_3_commands_and_converser.py Remove settings option --- cogs/gpt_3_commands_and_converser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cogs/gpt_3_commands_and_converser.py b/cogs/gpt_3_commands_and_converser.py index 05d5248..96687bd 100644 --- a/cogs/gpt_3_commands_and_converser.py +++ b/cogs/gpt_3_commands_and_converser.py @@ -739,7 +739,6 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): "summarize_conversations", "summarize_threshold", "IMAGE_SAVE_PATH", - "openai_key", ], ) @discord.option( From 5561ff3b5748e0fae1e960450892c494727fc16e Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 31 Dec 2022 15:26:27 -0500 Subject: [PATCH 04/12] Allow for /g to be used while in a conversation independently by the same user without conflicts --- cogs/gpt_3_commands_and_converser.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cogs/gpt_3_commands_and_converser.py b/cogs/gpt_3_commands_and_converser.py index af1e40f..7b5a5c9 100644 --- a/cogs/gpt_3_commands_and_converser.py +++ b/cogs/gpt_3_commands_and_converser.py @@ -499,7 +499,7 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): ) # ctx can be of type AppContext(interaction) or Message - async def encapsulated_send(self, user_id, prompt, ctx, response_message=None): + async def encapsulated_send(self, user_id, prompt, ctx, response_message=None, from_g_command=False): new_prompt = prompt + "\nGPTie: " from_context = isinstance(ctx, discord.ApplicationContext) @@ -510,6 +510,7 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): if ( user_id in self.conversating_users and tokens > self.model.summarize_threshold + and not from_g_command ): # We don't need to worry about the differences between interactions and messages in this block, @@ -561,7 +562,7 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): ) # If the user is conversing, add the GPT response to their conversation history. - if user_id in self.conversating_users: + if user_id in self.conversating_users and not from_g_command: self.conversating_users[user_id].history.append( "\nGPTie: " + str(response_text) + "<|endofstatement|>\n" ) @@ -647,10 +648,8 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): await self.encapsulated_send( user.id, - prompt - if user.id not in self.conversating_users - else "".join(self.conversating_users[user.id].history), - ctx, + prompt, + ctx, from_g_command=True ) @discord.slash_command( From a4436f235f7e317f14fa0b996377428ebb685887 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sat, 31 Dec 2022 20:26:47 +0000 Subject: [PATCH 05/12] Format Python code with psf/black push --- cogs/gpt_3_commands_and_converser.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/cogs/gpt_3_commands_and_converser.py b/cogs/gpt_3_commands_and_converser.py index 7b5a5c9..8f6bcb0 100644 --- a/cogs/gpt_3_commands_and_converser.py +++ b/cogs/gpt_3_commands_and_converser.py @@ -499,7 +499,9 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): ) # ctx can be of type AppContext(interaction) or Message - async def encapsulated_send(self, user_id, prompt, ctx, response_message=None, from_g_command=False): + async def encapsulated_send( + self, user_id, prompt, ctx, response_message=None, from_g_command=False + ): new_prompt = prompt + "\nGPTie: " from_context = isinstance(ctx, discord.ApplicationContext) @@ -646,11 +648,7 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): # Send the request to the model # If conversing, the prompt to send is the history, otherwise, it's just the prompt - await self.encapsulated_send( - user.id, - prompt, - ctx, from_g_command=True - ) + await self.encapsulated_send(user.id, prompt, ctx, from_g_command=True) @discord.slash_command( name="chat-gpt", From 3b1f8424f387afd7791c39079dab280f77580e5c Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 31 Dec 2022 16:35:30 -0500 Subject: [PATCH 06/12] Remove GPTie: pre-prompt for /g, show the prompt after /g is run --- README.md | 4 ++++ cogs/gpt_3_commands_and_converser.py | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a8fb08..d0c519b 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,10 @@ Both the OpenAI API key and the Discord bot token needed to be loaded into a .en You also need to add a DEBUG_GUILD id and a DEBUG_CHANNEL id, the debug guild id is a server id, and the debug channel id is a text-channel id in Discord. Your final .env file should look like the following: +You also need to add the allowed guilds that the bot can operate on, this is the `ALLOWED_GUILDS` field, to get a guild ID, right click a server and click "Copy ID". + +You also need to add the roles that can use the bot, this is the `ALLOWED_ROLES` field, enter role names here, separated by commas. Currently, there is no way to give everybody access to the bot, and you have to use roles, but it will be done soon. + ``` OPENAI_TOKEN="" DISCORD_TOKEN="" diff --git a/cogs/gpt_3_commands_and_converser.py b/cogs/gpt_3_commands_and_converser.py index 8f6bcb0..762f4b3 100644 --- a/cogs/gpt_3_commands_and_converser.py +++ b/cogs/gpt_3_commands_and_converser.py @@ -502,7 +502,8 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): async def encapsulated_send( self, user_id, prompt, ctx, response_message=None, from_g_command=False ): - new_prompt = prompt + "\nGPTie: " + new_prompt = prompt + "\nGPTie: " if not from_g_command else prompt + from_context = isinstance(ctx, discord.ApplicationContext) try: @@ -556,6 +557,11 @@ class GPT3ComCon(commands.Cog, name="GPT3ComCon"): response_text = response_text.replace("GPTie: ", "") response_text = response_text.replace("<|endofstatement|>", "") + if from_g_command: + # Append the prompt to the beginning of the response, in italics, then a new line + response_text = response_text.strip() + response_text = f"***{prompt}***\n\n{response_text}" + # If GPT3 tries to ping somebody, don't let it happen if re.search(r"<@!?\d+>|<@&\d+>|<#\d+>", str(response_text)): message = "I'm sorry, I can't mention users, roles, or channels." From 7679f56dae51586b40721c4ca3e6b3950ca59c54 Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 31 Dec 2022 16:46:34 -0500 Subject: [PATCH 07/12] bump version --- gpt3discord.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gpt3discord.py b/gpt3discord.py index 0d4c2e6..6cb0da1 100644 --- a/gpt3discord.py +++ b/gpt3discord.py @@ -15,7 +15,7 @@ from models.message_model import Message from models.openai_model import Model from models.usage_service_model import UsageService -__version__ = "2.0.1" +__version__ = "2.0.2" load_dotenv() import os From cf27b0f0f0e7b7111c1914a2809619a346ae3854 Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 31 Dec 2022 17:27:55 -0500 Subject: [PATCH 08/12] Activity presence --- gpt3discord.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gpt3discord.py b/gpt3discord.py index 6cb0da1..025de48 100644 --- a/gpt3discord.py +++ b/gpt3discord.py @@ -31,7 +31,8 @@ asyncio.ensure_future(Deletion.process_deletion_queue(deletion_queue, 1, 1)) """ Settings for the bot """ -bot = commands.Bot(intents=discord.Intents.all(), command_prefix="!") +activity = discord.Activity(type=discord.ActivityType.watching, name="for /help /g, and more!") +bot = commands.Bot(intents=discord.Intents.all(), command_prefix="!", activity=activity) usage_service = UsageService(Path(os.environ.get("DATA_DIR", os.getcwd()))) model = Model(usage_service) From eb407907d0b4ca36247aeca26508d368586335f7 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sat, 31 Dec 2022 22:28:14 +0000 Subject: [PATCH 09/12] Format Python code with psf/black push --- gpt3discord.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gpt3discord.py b/gpt3discord.py index 025de48..7171d48 100644 --- a/gpt3discord.py +++ b/gpt3discord.py @@ -31,7 +31,9 @@ asyncio.ensure_future(Deletion.process_deletion_queue(deletion_queue, 1, 1)) """ Settings for the bot """ -activity = discord.Activity(type=discord.ActivityType.watching, name="for /help /g, and more!") +activity = discord.Activity( + type=discord.ActivityType.watching, name="for /help /g, and more!" +) bot = commands.Bot(intents=discord.Intents.all(), command_prefix="!", activity=activity) usage_service = UsageService(Path(os.environ.get("DATA_DIR", os.getcwd()))) model = Model(usage_service) From 1b036ef9027c3374405eb32b791a55ea3bc00b9b Mon Sep 17 00:00:00 2001 From: Cooper Ry Lees Date: Sat, 31 Dec 2022 16:22:22 -0800 Subject: [PATCH 10/12] Fix copy of cogs + models source code - We were not putting the modules in a dir so it was not being included in the install - Fix the COPY directives - Add a `find` run to show the src layout so someone building / looking at logs can inspect easy Ran and got a new error which I will open an issue for using your recent additons ... --- Dockerfile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index ab591ca..d7a445d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,10 +11,12 @@ RUN pip install --target="/install" --upgrade pip setuptools wheel COPY requirements.txt /install RUN pip install --target="/install" -r requirements.txt COPY README.md /src -COPY cogs /src -COPY models /src +COPY cogs /src/cogs +COPY models /src/models COPY gpt3discord.py /src COPY pyproject.toml /src +# For debugging + seeing that the modiles file layouts look correct ... +find /src RUN pip install --target="/install" /src # Copy minimal to main image (to keep as small as possible) @@ -22,4 +24,4 @@ FROM python:${PY_VERSION}-slim ARG PY_VERSION COPY --from=builder /install /usr/local/lib/python${PY_VERSION}/site-packages COPY gpt3discord.py /bin/gpt3discord -CMD ["python3", "/bin/gpt3discord"] \ No newline at end of file +CMD ["python3", "/bin/gpt3discord"] From e31df356a9b4f04191a43e7f4e8d2b18b1cd9f45 Mon Sep 17 00:00:00 2001 From: Rene Teigen Date: Sun, 1 Jan 2023 00:25:31 +0000 Subject: [PATCH 11/12] Changed send_followup to respond --- models/check_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/check_model.py b/models/check_model.py index c184404..3788374 100644 --- a/models/check_model.py +++ b/models/check_model.py @@ -11,7 +11,7 @@ class Check: async def inner(ctx: discord.ApplicationContext): if not any(role.name in ALLOWED_ROLES for role in ctx.user.roles): await ctx.defer(ephemeral=True) - await ctx.send_followup( + await ctx.respond( "You don't have permission to use this.", ephemeral=True, delete_after=10, From 00cdd545426f3483503fb53f3e32a0637eaa29f1 Mon Sep 17 00:00:00 2001 From: Kaveen Kumarasinghe Date: Sat, 31 Dec 2022 19:53:43 -0500 Subject: [PATCH 12/12] Possibly fix find /src command? --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d7a445d..8bd1a09 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ COPY models /src/models COPY gpt3discord.py /src COPY pyproject.toml /src # For debugging + seeing that the modiles file layouts look correct ... -find /src +RUN find /src RUN pip install --target="/install" /src # Copy minimal to main image (to keep as small as possible)