You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

881 lines
26 KiB

import discord
from pycord.multicog import add_to_group
from services.environment_service import EnvService
from models.check_model import Check
from models.autocomplete_model import (
Settings_autocompleter,
File_autocompleter,
Translations_autocompleter,
)
ALLOWED_GUILDS = EnvService.get_allowed_guilds()
class Commands(discord.Cog, name="Commands"):
"""Cog containing all slash and context commands as one-liners"""
def __init__(
self,
bot,
usage_service,
model,
message_queue,
deletion_queue,
converser_cog,
image_draw_cog,
image_service_cog,
moderations_cog,
index_cog,
translations_cog=None,
search_cog=None,
):
super().__init__()
self.bot = bot
self.usage_service = usage_service
self.model = model
self.message_queue = message_queue
self.deletion_queue = deletion_queue
self.converser_cog = converser_cog
self.image_draw_cog = image_draw_cog
self.image_service_cog = image_service_cog
self.moderations_cog = moderations_cog
self.index_cog = index_cog
self.translations_cog = translations_cog
self.search_cog = search_cog
# Create slash command groups
dalle = discord.SlashCommandGroup(
name="dalle",
description="Dalle related commands",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_dalle_roles()],
)
gpt = discord.SlashCommandGroup(
name="gpt",
description="GPT related commands",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_gpt_roles()],
)
system = discord.SlashCommandGroup(
name="system",
description="Admin/System settings for the bot",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_admin_roles()],
)
mod = discord.SlashCommandGroup(
name="mod",
description="AI-Moderation commands for the bot",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_admin_roles()],
)
index = discord.SlashCommandGroup(
name="index",
description="Custom index commands for the bot",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_index_roles()],
)
#
# System commands
#
@add_to_group("system")
@discord.slash_command(
name="settings",
description="Get settings for GPT3Discord",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="parameter",
description="The setting to change",
required=False,
autocomplete=Settings_autocompleter.get_settings,
)
@discord.option(
name="value",
description="The value to set the setting to",
required=False,
autocomplete=Settings_autocompleter.get_value,
)
@discord.guild_only()
async def settings(
self, ctx: discord.ApplicationContext, parameter: str = None, value: str = None
):
await self.converser_cog.settings_command(ctx, parameter, value)
@add_to_group("system")
@discord.slash_command(
name="settings-reset",
description="Reset all settings for GPT3Discord",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
async def settings_reset(self, ctx: discord.ApplicationContext):
await self.converser_cog.settings_reset_command(ctx)
@add_to_group("system")
@discord.slash_command(
name="local-size",
description="Get the size of the dall-e images folder that we have on the current system",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
async def local_size(self, ctx: discord.ApplicationContext):
await self.image_draw_cog.local_size_command(ctx)
@add_to_group("system")
@discord.slash_command(
name="clear-local",
description="Clear the local dalleimages folder on system.",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
async def clear_local(self, ctx: discord.ApplicationContext):
await self.image_draw_cog.clear_local_command(ctx)
@add_to_group("system")
@discord.slash_command(
name="usage",
description="Get usage statistics for GPT3Discord",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
async def usage(self, ctx: discord.ApplicationContext):
await self.converser_cog.usage_command(ctx)
@add_to_group("system")
@discord.slash_command(
name="set-usage",
description="Set the current OpenAI usage (in dollars)",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="usage_amount",
description="The current usage amount in dollars and cents (e.g 10.24)",
type=float,
)
async def set_usage(self, ctx: discord.ApplicationContext, usage_amount: float):
await self.converser_cog.set_usage_command(ctx, usage_amount)
@add_to_group("system")
@discord.slash_command(
name="delete-conversation-threads",
description="Delete all conversation threads across the bot servers.",
guild_ids=ALLOWED_GUILDS,
)
async def delete_all_conversation_threads(self, ctx: discord.ApplicationContext):
await self.converser_cog.delete_all_conversation_threads_command(ctx)
# """
# Moderation commands
# """
@add_to_group("mod")
@discord.slash_command(
name="test",
description="Used to test a prompt and see what threshold values are returned by the moderations endpoint",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="prompt",
description="The prompt to test",
required=True,
)
@discord.guild_only()
async def moderations_test(self, ctx: discord.ApplicationContext, prompt: str):
await self.moderations_cog.moderations_test_command(ctx, prompt)
@add_to_group("mod")
@discord.slash_command(
name="set",
description="Turn the moderations service on and off",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="status",
description="Enable or disable the moderations service for the current guild (on/off)",
required=True,
choices=["on", "off"],
)
@discord.option(
name="alert_channel_id",
description="The channel ID to send moderation alerts to",
required=False,
autocomplete=Settings_autocompleter.get_value_alert_id_channel,
)
@discord.guild_only()
async def moderations(
self, ctx: discord.ApplicationContext, status: str, alert_channel_id: str
):
await self.moderations_cog.moderations_command(ctx, status, alert_channel_id)
@add_to_group("mod")
@discord.slash_command(
name="config",
description="Configure the moderations service for the current guild. Lower # = more strict",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="type",
description="The type of moderation to configure",
required=True,
autocomplete=Settings_autocompleter.get_value_moderations,
)
@discord.option(
name="hate",
description="The threshold for hate speech",
required=False,
min_value=0,
max_value=1,
)
@discord.option(
name="hate_threatening",
description="The threshold for hate/threatening speech",
required=False,
min_value=0,
max_value=1,
)
@discord.option(
name="self_harm",
description="The threshold for self_harm speech",
required=False,
min_value=0,
max_value=1,
)
@discord.option(
name="sexual",
description="The threshold for sexual speech",
required=False,
min_value=0,
max_value=1,
)
@discord.option(
name="sexual_minors",
description="The threshold for sexual speech with minors in context",
required=False,
min_value=0,
max_value=1,
)
@discord.option(
name="violence",
description="The threshold for violent speech",
required=False,
min_value=0,
max_value=1,
)
@discord.option(
name="violence_graphic",
description="The threshold for violent and graphic speech",
required=False,
min_value=0,
max_value=1,
)
@discord.guild_only()
async def config(
self,
ctx: discord.ApplicationContext,
type: str,
hate: float,
hate_threatening: float,
self_harm: float,
sexual: float,
sexual_minors: float,
violence: float,
violence_graphic: float,
):
await self.moderations_cog.config_command(
ctx,
type,
hate,
hate_threatening,
self_harm,
sexual,
sexual_minors,
violence,
violence_graphic,
)
#
# GPT commands
#
@add_to_group("gpt")
@discord.slash_command(
name="ask",
description="Ask GPT3 something!",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="prompt", description="The prompt to send to GPT3", required=True
)
@discord.option(
name="private", description="Will only be visible to you", required=False
)
@discord.option(
name="temperature",
description="Higher values means the model will take more risks",
required=False,
min_value=0,
max_value=2,
)
@discord.option(
name="top_p",
description="1 is greedy sampling, 0.1 means only considering the top 10% of probability distribution",
required=False,
min_value=0,
max_value=1,
)
@discord.option(
name="frequency_penalty",
description="Decreasing the model's likelihood to repeat the same line verbatim",
required=False,
min_value=-2,
max_value=2,
)
@discord.option(
name="presence_penalty",
description="Increasing the model's likelihood to talk about new topics",
required=False,
min_value=-2,
max_value=2,
)
@discord.guild_only()
async def ask(
self,
ctx: discord.ApplicationContext,
prompt: str,
private: bool,
temperature: float,
top_p: float,
frequency_penalty: float,
presence_penalty: float,
):
await self.converser_cog.ask_command(
ctx,
prompt,
private,
temperature,
top_p,
frequency_penalty,
presence_penalty,
)
@add_to_group("gpt")
@discord.slash_command(
name="edit",
description="Ask GPT3 to edit some text!",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="instruction",
description="How you want GPT3 to edit the text",
required=True,
)
@discord.option(
name="text",
description="The text you want to edit, can be empty",
required=False,
default="",
)
@discord.option(
name="private", description="Will only be visible to you", required=False
)
@discord.option(
name="temperature",
description="Higher values means the model will take more risks",
required=False,
input_type=float,
min_value=0,
max_value=2,
)
@discord.option(
name="top_p",
description="1 is greedy sampling, 0.1 means only considering the top 10% of probability distribution",
required=False,
input_type=float,
min_value=0,
max_value=1,
)
@discord.option(
name="codex", description="Enable codex version", required=False, default=False
)
@discord.guild_only()
async def edit(
self,
ctx: discord.ApplicationContext,
instruction: str,
text: str,
private: bool,
temperature: float,
top_p: float,
codex: bool,
):
await self.converser_cog.edit_command(
ctx, instruction, text, private, temperature, top_p, codex
)
@add_to_group("gpt")
@discord.slash_command(
name="converse",
description="Have a conversation with GPT3",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="opener",
description="Which sentence to start with, added after the file",
required=False,
)
@discord.option(
name="opener_file",
description="Which file to start with, added before the opener, sets minimal starter",
required=False,
autocomplete=File_autocompleter.get_openers,
)
@discord.option(
name="private",
description="Converse in a private thread",
required=False,
default=False,
)
@discord.option(
name="minimal",
description="Use minimal starter text, saves tokens and has a more open personality",
required=False,
default=False,
)
@discord.option(
name="model",
description="Which model to use with the bot",
required=False,
default=False,
autocomplete=Settings_autocompleter.get_models,
)
@discord.option(
name="temperature",
description="Higher values means the model will take more risks",
required=False,
input_type=float,
min_value=0,
max_value=2,
)
@discord.option(
name="top_p",
description="1 is greedy sampling, 0.1 means only top 10%",
required=False,
input_type=float,
min_value=0,
max_value=1,
)
@discord.option(
name="frequency_penalty",
description="Decreasing the model's likelihood to repeat the same line verbatim",
required=False,
input_type=float,
min_value=-2,
max_value=2,
)
@discord.option(
name="presence_penalty",
description="Increasing the model's likelihood to talk about new topics",
required=False,
input_type=float,
min_value=-2,
max_value=2,
)
@discord.guild_only()
async def converse(
self,
ctx: discord.ApplicationContext,
opener: str,
opener_file: str,
private: bool,
minimal: bool,
model: str,
temperature: float,
top_p: float,
frequency_penalty: float,
presence_penalty: float,
):
await self.converser_cog.converse_command(
ctx,
opener,
opener_file,
private,
minimal,
model,
temperature,
top_p,
frequency_penalty,
presence_penalty,
)
@add_to_group("gpt")
@discord.slash_command(
name="end",
description="End a conversation with GPT3",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
async def end(self, ctx: discord.ApplicationContext):
await self.converser_cog.end_command(ctx)
#
# Index commands
#
@add_to_group("index")
@discord.slash_command(
name="load",
description="Select one of your saved indexes to query from",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
@discord.option(
name="user_index",
description="Which user file to load the index from",
required=False,
autocomplete=File_autocompleter.get_user_indexes,
)
@discord.option(
name="server_index",
description="Which server file to load the index from",
required=False,
autocomplete=File_autocompleter.get_server_indexes,
)
@discord.option(
name="search_index",
description="Which search index file to load the index from",
required=False,
autocomplete=File_autocompleter.get_user_search_indexes,
)
async def load_index(
self,
ctx: discord.ApplicationContext,
user_index: str,
server_index: str,
search_index: str,
):
await ctx.defer()
await self.index_cog.load_index_command(
ctx, user_index, server_index, search_index
)
@add_to_group("index")
@discord.slash_command(
name="add", description="Add an index to query from", guild_ids=ALLOWED_GUILDS
)
@discord.guild_only()
@discord.option(
name="file",
description="A file to create the index from",
required=False,
input_type=discord.SlashCommandOptionType.attachment,
)
@discord.option(
name="link",
description="A link to a file to a webpage ",
required=False,
input_type=str,
)
async def set_file(
self, ctx: discord.ApplicationContext, file: discord.Attachment, link: str
):
await self.index_cog.set_index_command(ctx, file, link)
@add_to_group("index")
@discord.slash_command(
name="reset",
description="Reset (delete) all of your saved indexes",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
async def reset(self, ctx: discord.ApplicationContext):
await self.index_cog.reset_command(ctx)
@add_to_group("index")
@discord.slash_command(
name="compose",
description="Combine multiple indexes together",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="name",
description="The name of the new index",
required=False,
input_type=discord.SlashCommandOptionType.string,
)
@discord.guild_only()
async def compose(self, ctx: discord.ApplicationContext, name: str):
await self.index_cog.compose_command(ctx, name)
@add_to_group("index")
@discord.slash_command(
name="add_discord",
description="Set a index from a discord channel",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
@discord.option(
name="channel",
description="A channel to create the index from",
required=False,
input_type=discord.SlashCommandOptionType.channel,
)
async def set_discord(
self, ctx: discord.ApplicationContext, channel: discord.TextChannel
):
await self.index_cog.set_discord_command(ctx, channel)
@add_to_group("index")
@discord.slash_command(
name="discord_backup",
description="Save an index made from the whole server",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_admin_roles(), Check.check_index_roles()],
)
@discord.guild_only()
async def discord_backup(self, ctx: discord.ApplicationContext):
await self.index_cog.discord_backup_command(ctx)
@add_to_group("index")
@discord.slash_command(
name="query", description="Query from your index", guild_ids=ALLOWED_GUILDS
)
@discord.guild_only()
@discord.option(name="query", description="What to query the index", required=True)
@discord.option(
name="nodes",
description="How many nodes should the response be queried from, only non-deep indexes",
required=False,
default=1,
min_value=1,
max_value=3,
input_type=discord.SlashCommandOptionType.integer,
)
@discord.option(
name="response_mode",
description="Response mode, doesn't work on deep composed indexes",
guild_ids=ALLOWED_GUILDS,
required=False,
default="default",
choices=["default", "compact", "tree_summarize"],
)
async def query(
self,
ctx: discord.ApplicationContext,
query: str,
nodes: int,
response_mode: str,
):
await ctx.defer()
await self.index_cog.query_command(ctx, query, nodes, response_mode)
#
# DALLE commands
#
@add_to_group("dalle")
@discord.slash_command(
name="draw",
description="Draw an image from a prompt",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(name="prompt", description="The prompt to draw from", required=True)
async def draw(self, ctx: discord.ApplicationContext, prompt: str):
await self.image_draw_cog.draw_command(ctx, prompt)
@add_to_group("dalle")
@discord.slash_command(
name="optimize",
description="Optimize a text prompt for DALL-E/MJ/SD image generation.",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(
name="prompt", description="The text prompt to optimize.", required=True
)
@discord.guild_only()
async def optimize(self, ctx: discord.ApplicationContext, prompt: str):
await self.image_service_cog.optimize_command(ctx, prompt)
#
# Other commands
#
@discord.slash_command(
name="private-test",
description="Private thread for testing. Only visible to you and server admins.",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
async def private_test(self, ctx: discord.ApplicationContext):
await self.converser_cog.private_test_command(ctx)
@discord.slash_command(
name="help", description="Get help for GPT3Discord", guild_ids=ALLOWED_GUILDS
)
@discord.guild_only()
async def help(self, ctx: discord.ApplicationContext):
await self.converser_cog.help_command(ctx)
@discord.slash_command(
name="setup",
description="Setup your API key for use with GPT3Discord",
guild_ids=ALLOWED_GUILDS,
)
@discord.guild_only()
async def setup(self, ctx: discord.ApplicationContext):
await self.converser_cog.setup_command(ctx)
#
# Text-based context menu commands from here
#
@discord.message_command(
name="Ask GPT", guild_ids=ALLOWED_GUILDS, checks=[Check.check_gpt_roles()]
)
async def ask_gpt_action(self, ctx, message: discord.Message):
await self.converser_cog.ask_gpt_action(ctx, message)
#
# Image-based context menu commands from here
#
@discord.message_command(
name="Draw", guild_ids=ALLOWED_GUILDS, checks=[Check.check_dalle_roles()]
)
async def draw_action(self, ctx, message: discord.Message):
await self.image_draw_cog.draw_action(ctx, message)
"""
Translation commands and actions
"""
@discord.slash_command(
name="translate",
description="Translate text to a given language",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_translator_roles()],
)
@discord.option(name="text", description="The text to translate", required=True)
@discord.option(
name="target_language",
description="The language to translate to",
required=True,
autocomplete=Translations_autocompleter.get_languages,
)
@discord.option(
name="formality",
description="Formal/Informal tone of translation",
required=False,
autocomplete=Translations_autocompleter.get_formality_values,
)
@discord.guild_only()
async def translate(
self,
ctx: discord.ApplicationContext,
text: str,
target_language: str,
formality: str,
):
if self.translations_cog:
await self.translations_cog.translate_command(
ctx, text, target_language, formality
)
else:
await ctx.respond(
"Translations are disabled on this server.", ephemeral=True
)
@discord.slash_command(
name="languages",
description="View the supported languages for translation",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_translator_roles()],
)
@discord.guild_only()
async def languages(self, ctx: discord.ApplicationContext):
if self.translations_cog:
await self.translations_cog.languages_command(ctx)
else:
await ctx.respond(
"Translations are disabled on this server.", ephemeral=True
)
@discord.message_command(
name="Translate",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_translator_roles()],
)
async def translate_action(self, ctx, message: discord.Message):
if self.translations_cog:
await self.translations_cog.translate_action(ctx, message)
else:
await ctx.respond(
"Translations are disabled on this server.", ephemeral=True
)
# @discord.message_command(
# name="Paraphrase",
# guild_ids=ALLOWED_GUILDS,
# checks=[Check.check_gpt_roles()],
# )
# async def paraphrase_action(self, ctx, message: discord.Message):
# await self.converser_cog.paraphrase_action(ctx, message)
@discord.message_command(
name="Elaborate",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_gpt_roles()],
)
async def elaborate_action(self, ctx, message: discord.Message):
await self.converser_cog.elaborate_action(ctx, message)
@discord.message_command(
name="Summarize",
guild_ids=ALLOWED_GUILDS,
checks=[Check.check_gpt_roles()],
)
async def summarize_action(self, ctx, message: discord.Message):
await self.converser_cog.summarize_action(ctx, message)
# Search slash commands
@discord.slash_command(
name="search",
description="Search google alongside GPT3 for something",
guild_ids=ALLOWED_GUILDS,
)
@discord.option(name="query", description="The query to search", required=True)
@discord.option(
name="scope",
description="How many top links to use for context",
required=False,
input_type=discord.SlashCommandOptionType.integer,
max_value=6,
min_value=1,
)
@discord.option(
name="nodes",
description="The higher the number, the more accurate the results, but more expensive",
required=False,
input_type=discord.SlashCommandOptionType.integer,
max_value=4,
min_value=1,
)
@discord.option(
name="deep",
description="Do a more intensive, long-running search",
required=False,
input_type=discord.SlashCommandOptionType.boolean,
)
@discord.guild_only()
async def search(
self,
ctx: discord.ApplicationContext,
query: str,
scope: int,
nodes: int,
deep: bool,
):
await self.search_cog.search_command(ctx, query, scope, nodes, deep)