Added function calling and lots of other stuff

This commit is contained in:
Paillat
2023-07-15 12:20:38 +02:00
parent 0ce8e2d6d3
commit 4f9a7eb0a6
28 changed files with 434 additions and 358 deletions

6
src/cogs/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
from src.cogs.setup import Setup
from src.cogs.settings import Settings
from src.cogs.help import Help
from src.cogs.chat import Chat
from src.cogs.manage_chat import ManageChat
from src.cogs.moderation import Moderation

134
src/cogs/chat.py Normal file
View File

@@ -0,0 +1,134 @@
import discord
from discord.ext import commands
from src.config import (
debug,
webhook_url,
)
import src.makeprompt as mp
import aiohttp
class MyModal(discord.ui.Modal):
def __init__(self, message):
super().__init__(title="Downvote")
self.add_item(
discord.ui.InputText(label="Reason", style=discord.InputTextStyle.long)
)
self.message = message
async def callback(self, interaction: discord.Interaction):
debug("Downvote sent !")
embed = discord.Embed(
title="Thanks for your feedback !",
description="Your downvote has been sent to the developers. Thanks for your help !",
color=discord.Color.og_blurple(),
)
embed.add_field(name="Message", value=self.children[0].value)
await interaction.response.send_message(embed=embed, ephemeral=True)
if webhook_url != "" and webhook_url != None:
session = aiohttp.ClientSession()
webhook = discord.Webhook.from_url(webhook_url, session=session)
embed = discord.Embed(
title="Downvote",
description=f"Downvote recieved!",
color=discord.Color.og_blurple(),
)
embed.add_field(name="Reason", value=self.children[0].value, inline=True)
embed.add_field(name="Author", value=interaction.user.mention, inline=True)
embed.add_field(
name="Channel", value=self.message.channel.name, inline=True
)
embed.add_field(name="Guild", value=self.message.guild.name, inline=True)
history = await self.message.channel.history(
limit=5, before=self.message
).flatten()
history.reverse()
users = []
fake_users = []
for user in history:
if user.author not in users:
# we anonimize the user, so user1, user2, user3, etc
fake_users.append(f"user{len(fake_users)+1}")
users.append(user.author)
if self.message.author not in users:
fake_users.append(f"user{len(fake_users)+1}")
users.append(self.message.author)
for msg in history:
uname = fake_users[users.index(msg.author)]
if len(msg.content) > 1023:
embed.add_field(
name=f"{uname} said", value=msg.content[:1023], inline=False
)
else:
embed.add_field(
name=f"{uname} said", value=msg.content, inline=False
)
uname = fake_users[users.index(self.message.author)]
embed.add_field(
name=f"{uname} said",
value="*" + self.message.content[:1021] + "*"
if len(self.message.content) > 1021
else "*"
+ self.message.content
+ "*", # [:1021] if len(self.message.content) > 1021,
inline=False,
)
await webhook.send(embed=embed)
else:
debug(
"Error while sending webhook, probably no webhook is set up in the .env file"
)
class Chat(discord.Cog):
def __init__(self, bot: discord.Bot):
super().__init__()
self.bot = bot
@discord.Cog.listener()
async def on_message(self, message: discord.Message):
await mp.chat_process(self, message)
@discord.slash_command(name="say", description="Say a message")
async def say(self, ctx: discord.ApplicationContext, message: str):
await ctx.respond("Message sent !", ephemeral=True)
await ctx.send(message)
@discord.slash_command(name="redo", description="Redo a message")
async def redo(self, ctx: discord.ApplicationContext):
history = await ctx.channel.history(limit=2).flatten()
message_to_delete = history[0]
message_to_redo = history[1]
if message_to_delete.author.id == self.bot.user.id:
await message_to_delete.delete()
else:
message_to_redo = history[0]
await ctx.respond("Message redone !", ephemeral=True)
await mp.chat_process(self, message_to_redo)
@discord.message_command(name="Downvote", description="Downvote a message")
@commands.cooldown(1, 60, commands.BucketType.user)
async def downvote(self, ctx: discord.ApplicationContext, message: discord.Message):
if message.author.id == self.bot.user.id:
modal = MyModal(message)
await ctx.send_modal(modal)
else:
await ctx.respond(
"You can't downvote a message that is not from me !", ephemeral=True
)
@downvote.error
async def downvote_error(self, ctx, error):
if isinstance(error, commands.CommandOnCooldown):
await ctx.respond("You are on cooldown !", ephemeral=True)
else:
debug(error)
raise error

101
src/cogs/help.py Normal file
View File

@@ -0,0 +1,101 @@
import discord
class Help(discord.Cog):
def __init__(self, bot: discord.Bot) -> None:
super().__init__()
self.bot = bot
@discord.slash_command(name="help", description="Show all the commands")
async def help(self, ctx: discord.ApplicationContext):
embed = discord.Embed(
title="Help", description="Here is the help page", color=0x00FF00
)
embed.add_field(name="/setup", value="Setup the bot", inline=False)
embed.add_field(name="/enable", value="Enable the bot", inline=False)
embed.add_field(name="/disable", value="Disable the bot", inline=False)
embed.add_field(
name="/advanced", value="Set the advanced settings", inline=False
)
embed.add_field(
name="/advanced_help",
value="Get help about the advanced settings",
inline=False,
)
embed.add_field(
name="/enable_tts", value="Enable the Text To Speech", inline=False
)
embed.add_field(
name="/disable_tts", value="Disable the Text To Speech", inline=False
)
embed.add_field(
name="/add|remove_channel",
value="Add or remove a channel to the list of channels where the bot will answer. Only available on premium guilds",
inline=False,
)
embed.add_field(
name="/delete", value="Delete all your data from our server", inline=False
)
embed.add_field(
name="/cancel",
value="Cancel the last message sent by the bot",
inline=False,
)
embed.add_field(
name="/default",
value="Set the advanced settings to their default values",
inline=False,
)
embed.add_field(name="/say", value="Say a message", inline=False)
embed.add_field(
name="/redo", value="Redo the last message sent by the bot", inline=False
)
embed.add_field(
name="/moderation", value="Setup the AI auto-moderation", inline=False
)
embed.add_field(
name="/get_toxicity",
value="Get the toxicity that the AI would have given to a given message",
inline=False,
)
embed.add_field(name="/help", value="Show this message", inline=False)
# add a footer
embed.set_footer(text="Made by @Paillat#7777")
await ctx.respond(embed=embed, ephemeral=True)
@discord.slash_command(
name="advanced_help", description="Show the advanced settings meanings"
)
async def advanced_help(self, ctx: discord.ApplicationContext):
embed = discord.Embed(
title="Advanced Help",
description="Here is the advanced help page",
color=0x00FF00,
)
embed.add_field(
name="temperature",
value="The higher the temperature, the more likely the model will take risks. Conversely, a lower temperature will make the model more conservative. The default value is 0.9",
inline=False,
)
embed.add_field(
name="max_tokens",
value="The maximum number of tokens to generate. Higher values will result in more coherent text, but will take longer to complete. (default: 50). **Lower values will result in somentimes cutting off the end of the answer, but will be faster.**",
inline=False,
)
embed.add_field(
name="frequency_penalty",
value="The higher the frequency penalty, the more new words the model will introduce (default: 0.0)",
inline=False,
)
embed.add_field(
name="presence_penalty",
value="The higher the presence penalty, the more new words the model will introduce (default: 0.0)",
inline=False,
)
embed.add_field(
name="prompt_size",
value="The number of messages to use as a prompt (default: 5). The more messages, the more coherent the text will be, but the more it will take to generate and the more it will cost.",
inline=False,
)
# add a footer
embed.set_footer(text="Made by @Paillat#7777")
await ctx.respond(embed=embed, ephemeral=True)

108
src/cogs/manage_chat.py Normal file
View File

@@ -0,0 +1,108 @@
import discord
import re
import os
from src.config import debug, curs_data
class ManageChat(discord.Cog):
def __init__(self, bot: discord.Bot) -> None:
super().__init__()
self.bot = bot
@discord.slash_command(
name="cancel", description="Cancel the last message sent into a channel"
)
async def cancel(self, ctx: discord.ApplicationContext):
debug(
f"The user {ctx.author} ran the cancel command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}"
)
# check if the guild is in the database
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,))
if curs_data.fetchone() is None:
await ctx.respond(
"This server is not setup, please run /setup", ephemeral=True
)
return
# get the last message sent by the bot in the cha where the command was sent
last_message = await ctx.channel.fetch_message(ctx.channel.last_message_id)
# delete the message
await last_message.delete()
await ctx.respond("The last message has been deleted", ephemeral=True)
# add a slash command called "clear" that deletes all the messages in the channel
@discord.slash_command(
name="clear", description="Clear all the messages in the channel"
)
async def clear(self, ctx: discord.ApplicationContext):
debug(
f"The user {ctx.author.name} ran the clear command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}"
)
await ctx.respond("messages deleted!", ephemeral=True)
return await ctx.channel.purge()
@discord.slash_command(
name="transcript",
description="Get a transcript of the messages that have been sent in this channel intoa text file",
)
@discord.option(
name="channel_send",
description="The channel to send the transcript to",
required=False,
)
async def transcript(
self, ctx: discord.ApplicationContext, channel_send: discord.TextChannel = None
):
debug(
f"The user {ctx.author.name} ran the transcript command command in the channel {ctx.channel} of the guild {ctx.guild}, named {ctx.guild.name}"
)
# save all the messages in the channel in a txt file and send it
messages = await ctx.channel.history(limit=None).flatten()
messages.reverse()
transcript = ""
# defer the response
await ctx.defer() # defer the response so that the bot doesn't say that it's thinking
for msg in messages:
if msg.author.bot:
transcript += f"Botator: {msg.content}\n"
else:
mentions = re.findall(r"<@!?\d+>", msg.content)
# then replace each mention with the name of the user
for mention in mentions:
# get the user id
id = mention[2:-1]
# get the user
user = await self.bot.fetch_user(id)
# replace the mention with the name
msg.content = msg.content.replace(
mention, msg.guild.get_member(user.id).name
)
transcript += f"{msg.author.name}: {msg.content}\n"
# save the transcript in a txt file called transcript.txt. If the file already exists, delete it and create a new one
# check if the file exists
if os.path.exists("transcript.txt"):
os.remove("transcript.txt")
f = open("transcript.txt", "w")
f.write(transcript)
f.close()
last_message: discord.Message = await ctx.channel.fetch_message(
ctx.channel.last_message_id
)
new_file_name = f"transcript_{ctx.guild.name}_{ctx.channel.name}_{last_message.created_at.strftime('%d-%B-%Y')}.txt"
# rename the file with the name of the channel and the date in this format: transcript_servername_channelname_dd-month-yyyy.txt ex : transcript_Botator_Testing_12-may-2021.txt
os.rename(
"transcript.txt",
new_file_name,
)
# send the file in a private message to the user who ran the command
# TODO: rework so as to give the choice of a private send or a public send
if channel_send is None:
await ctx.respond(
file=discord.File(new_file_name),
ephemeral=True,
)
else:
await channel_send.send(file=discord.File(new_file_name))
await ctx.respond("Transcript sent!", ephemeral=True, delete_after=5)
await ctx.author.send(file=discord.File(new_file_name))
# delete the file
os.remove(new_file_name)

113
src/cogs/moderation.py Normal file
View File

@@ -0,0 +1,113 @@
import discord
from discord import default_permissions
import os
from src.config import debug, curs_data, con_data
import openai
import requests
class Moderation(discord.Cog):
def __init__(self, bot: discord.Bot) -> None:
super().__init__()
self.bot = bot
@discord.slash_command(
name="moderation", description="Enable or disable AI moderation & set the rules"
)
@discord.option(
name="enable",
description="Enable or disable AI moderation",
reqired=True,
)
@discord.option(
name="log_channel",
description="The channel where the moderation logs will be sent",
required=True,
)
@discord.option(
name="moderator_role", description="The role of the moderators", required=True
)
# the types of toxicity are 'requestedAttributes': {'TOXICITY': {}, 'SEVERE_TOXICITY': {}, 'IDENTITY_ATTACK': {}, 'INSULT': {}, 'PROFANITY': {}, 'THREAT': {}, 'SEXUALLY_EXPLICIT': {}, 'FLIRTATION': {}, 'OBSCENE': {}, 'SPAM': {}},
@discord.option(
name="toxicity", description="The toxicity threshold", required=False
)
@discord.option(
name="severe_toxicity",
description="The severe toxicity threshold",
required=False,
)
@discord.option(
name="identity_attack",
description="The identity attack threshold",
required=False,
)
@discord.option(name="insult", description="The insult threshold", required=False)
@discord.option(
name="profanity", description="The profanity threshold", required=False
)
@discord.option(name="threat", description="The threat threshold", required=False)
@discord.option(
name="sexually_explicit",
description="The sexually explicit threshold",
required=False,
)
@discord.option(
name="flirtation", description="The flirtation threshold", required=False
)
@discord.option(name="obscene", description="The obscene threshold", required=False)
@discord.option(name="spam", description="The spam threshold", required=False)
# we set the default permissions to the administrator permission, so only the server administrators can use this command
@default_permissions(administrator=True)
async def moderation(
self,
ctx: discord.ApplicationContext,
enable: bool,
log_channel: discord.TextChannel,
moderator_role: discord.Role,
toxicity: float = None,
severe_toxicity: float = None,
identity_attack: float = None,
insult: float = None,
profanity: float = None,
threat: float = None,
sexually_explicit: float = None,
flirtation: float = None,
obscene: float = None,
spam: float = None,
):
# local import, because we don't want to import the toxicity function if the moderation is disabled
# import toxicity as tox # this is a file called toxicity.py, which contains the toxicity function that allows you to check if a message is toxic or not (it uses the perspective api)
await ctx.respond(
"Our moderation capabilities have been switched to our new 100% free and open-source AI discord moderation bot! You add it to your server here: https://discord.com/api/oauth2/authorize?client_id=1071451913024974939&permissions=1377342450896&scope=bot and you can find the source code here: https://github.com/Paillat-dev/Moderator/ \n If you need help, you can join our support server here: https://discord.gg/pB6hXtUeDv",
ephemeral=True,
)
if enable == False:
curs_data.execute(
"DELETE FROM moderation WHERE guild_id = ?", (str(ctx.guild.id),)
)
con_data.commit()
await ctx.respond("Moderation disabled!", ephemeral=True)
return
@discord.slash_command(
name="get_toxicity", description="Get the toxicity of a message"
)
@discord.option(
name="message", description="The message you want to check", required=True
)
@default_permissions(administrator=True)
async def get_toxicity(self, ctx: discord.ApplicationContext, message: str):
await ctx.respond(
"Our moderation capabilities have been switched to our new 100% free and open-source AI discord moderation bot! You add it to your server here: https://discord.com/api/oauth2/authorize?client_id=1071451913024974939&permissions=1377342450896&scope=bot and you can find the source code here: https://discord.gg/pB6hXtUeDv . If you need help, you can join our support server here: https://discord.gg/pB6hXtUeDv",
ephemeral=True,
)
@discord.slash_command(
name="moderation_help", description="Get help with the moderation AI"
)
@default_permissions(administrator=True)
async def moderation_help(self, ctx: discord.ApplicationContext):
await ctx.respond(
"Our moderation capabilities have been switched to our new 100% free and open-source AI discord moderation bot! You add it to your server here: https://discord.com/api/oauth2/authorize?client_id=1071451913024974939&permissions=1377342450896&scope=bot and you can find the source code here: https://github.com/Paillat-dev/Moderator/ . If you need help, you can join our support server here: https://discord.gg/pB6hXtUeDv",
ephemeral=True,
)

393
src/cogs/settings.py Normal file
View File

@@ -0,0 +1,393 @@
import discord
from src.config import debug, con_data, curs_data, moderate, ctx_to_guid
from discord import default_permissions
models = ["davinci", "gpt-3.5-turbo", "gpt-4"]
images_recognition = ["enable", "disable"]
class Settings(discord.Cog):
def __init__(self, bot: discord.Bot) -> None:
super().__init__()
self.bot = bot
@discord.slash_command(name="advanced", description="Advanced settings")
@default_permissions(administrator=True)
@discord.option(name="max_tokens", description="The max tokens", required=False)
@discord.option(name="temperature", description="The temperature", required=False)
@discord.option(
name="frequency_penalty", description="The frequency penalty", required=False
)
@discord.option(
name="presence_penalty", description="The presence penalty", required=False
)
@discord.option(name="prompt_size", description="The prompt size", required=False)
async def advanced(
self,
ctx: discord.ApplicationContext,
max_tokens: int = None,
temperature: float = None,
frequency_penalty: float = None,
presence_penalty: float = None,
prompt_size: int = None,
):
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),))
if curs_data.fetchone() is None:
await ctx.respond("This server is not setup", ephemeral=True)
return
if (
max_tokens is None
and temperature is None
and frequency_penalty is None
and presence_penalty is None
and prompt_size is None
):
await ctx.respond("You must enter at least one argument", ephemeral=True)
return
if max_tokens is not None and (max_tokens < 1 or max_tokens > 4000):
await ctx.respond("Invalid max tokens", ephemeral=True)
return
if temperature is not None and (temperature < 0.0 or temperature > 1.0):
await ctx.respond("Invalid temperature", ephemeral=True)
return
if frequency_penalty is not None and (
frequency_penalty < 0.0 or frequency_penalty > 2.0
):
await ctx.respond("Invalid frequency penalty", ephemeral=True)
return
if presence_penalty is not None and (
presence_penalty < 0.0 or presence_penalty > 2.0
):
await ctx.respond("Invalid presence penalty", ephemeral=True)
return
if prompt_size is not None and (prompt_size < 1 or prompt_size > 10):
await ctx.respond("Invalid prompt size", ephemeral=True)
return
if max_tokens is None:
if (
curs_data.execute(
"SELECT max_tokens FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),)
).fetchone()[0]
is not None
and max_tokens is None
):
max_tokens = curs_data.execute(
"SELECT max_tokens FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),)
).fetchone()[0]
else:
max_tokens = 64
if temperature is None:
if (
curs_data.execute(
"SELECT temperature FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),)
).fetchone()[0]
is not None
and temperature is None
):
temperature = curs_data.execute(
"SELECT temperature FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),)
).fetchone()[0]
else:
temperature = 0.9
if frequency_penalty is None:
if (
curs_data.execute(
"SELECT frequency_penalty FROM data WHERE guild_id = ?",
(ctx_to_guid(ctx),),
).fetchone()[0]
is not None
and frequency_penalty is None
):
frequency_penalty = curs_data.execute(
"SELECT frequency_penalty FROM data WHERE guild_id = ?",
(ctx_to_guid(ctx),),
).fetchone()[0]
else:
frequency_penalty = 0.0
if presence_penalty is None:
if (
curs_data.execute(
"SELECT presence_penalty FROM data WHERE guild_id = ?",
(ctx_to_guid(ctx),),
).fetchone()[0]
is not None
and presence_penalty is None
):
presence_penalty = curs_data.execute(
"SELECT presence_penalty FROM data WHERE guild_id = ?",
(ctx_to_guid(ctx),),
).fetchone()[0]
else:
presence_penalty = 0.0
if prompt_size is None:
if (
curs_data.execute(
"SELECT prompt_size FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),)
).fetchone()[0]
is not None
and prompt_size is None
):
prompt_size = curs_data.execute(
"SELECT prompt_size FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),)
).fetchone()[0]
else:
prompt_size = 1
# update the database
curs_data.execute(
"UPDATE data SET max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?",
(
max_tokens,
temperature,
frequency_penalty,
presence_penalty,
prompt_size,
ctx_to_guid(ctx),
),
)
con_data.commit()
await ctx.respond("Advanced settings updated", ephemeral=True)
# create a command called "delete" that only admins can use wich deletes the guild id, the api key, the channel id and the advanced settings from the database
@discord.slash_command(name="default", description="Default settings")
@default_permissions(administrator=True)
async def default(self, ctx: discord.ApplicationContext):
# check if the guild is in the database
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),))
if curs_data.fetchone() is None:
await ctx.respond(
"This server is not setup, please run /setup", ephemeral=True
)
return
# set the advanced settings (max_tokens, temperature, frequency_penalty, presence_penalty, prompt_size) and also prompt_prefix to their default values
curs_data.execute(
"UPDATE data SET max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?",
(64, 0.9, 0.0, 0.0, 5, ctx_to_guid(ctx)),
)
con_data.commit()
await ctx.respond(
"The advanced settings have been set to their default values",
ephemeral=True,
)
# create a command called "cancel" that deletes the last message sent by the bot in the response channel
# when a message is sent into a channel check if the guild is in the database and if the bot is enabled
@discord.slash_command(
name="info", description="Show the information stored about this server"
)
@default_permissions(administrator=True)
async def info(self, ctx: discord.ApplicationContext):
# this command sends all the data about the guild, including the api key, the channel id, the advanced settings and the uses_count_today
# check if the guild is in the database
try:
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),))
data = curs_data.fetchone()
except:
data = None
if data[2] is None:
await ctx.respond("This server is not setup", ephemeral=True)
return
try:
curs_data.execute("SELECT * FROM model WHERE guild_id = ?", (ctx_to_guid(ctx),))
model = curs_data.fetchone()[1]
except:
model = None
if model is None:
model = "davinci"
embed = discord.Embed(
title="Info", description="Here is the info page", color=0x00FF00
)
embed.add_field(name="guild_id", value=data[0], inline=False)
embed.add_field(name="API Key", value="secret", inline=False)
embed.add_field(name="Main channel ID", value=data[1], inline=False)
embed.add_field(name="Model", value=model, inline=False)
embed.add_field(name="Is Active", value=data[3], inline=False)
embed.add_field(name="Max Tokens", value=data[4], inline=False)
embed.add_field(name="Temperature", value=data[5], inline=False)
embed.add_field(name="Frequency Penalty", value=data[6], inline=False)
embed.add_field(name="Presence Penalty", value=data[7], inline=False)
embed.add_field(name="Prompt Size", value=data[9], inline=False)
embed.add_field(name="Uses Count Today", value=data[8], inline=False)
if data[10]:
embed.add_field(name="Prompt prefix", value=data[10], inline=False)
await ctx.respond(embed=embed, ephemeral=True)
@discord.slash_command(name="prefix", description="Change the prefix of the prompt")
@default_permissions(administrator=True)
async def prefix(self, ctx: discord.ApplicationContext, prefix: str = ""):
try:
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),))
data = curs_data.fetchone()
api_key = data[2]
except:
await ctx.respond("This server is not setup", ephemeral=True)
return
if api_key is None or api_key == "":
await ctx.respond("This server is not setup", ephemeral=True)
return
if prefix != "":
await ctx.defer()
if await moderate(api_key=api_key, text=prefix):
await ctx.respond(
"This has been flagged as inappropriate by OpenAI, please choose another prefix",
ephemeral=True,
)
return
await ctx.respond("Prefix changed !", ephemeral=True, delete_after=5)
curs_data.execute(
"UPDATE data SET prompt_prefix = ? WHERE guild_id = ?",
(prefix, ctx_to_guid(ctx)),
)
con_data.commit()
# when someone mentions the bot, check if the guild is in the database and if the bot is enabled. If it is, send a message answering the mention
@discord.slash_command(
name="pretend", description="Make the bot pretend to be someone else"
)
@discord.option(
name="pretend to be...",
description="The person/thing you want the bot to pretend to be. Leave blank to disable pretend mode",
required=False,
)
@default_permissions(administrator=True)
async def pretend(self, ctx: discord.ApplicationContext, pretend_to_be: str = ""):
# check if the guild is in the database
try:
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),))
data = curs_data.fetchone()
api_key = data[2]
except:
await ctx.respond("This server is not setup", ephemeral=True)
return
if api_key is None or api_key == "":
await ctx.respond("This server is not setup", ephemeral=True)
return
if pretend_to_be is not None or pretend_to_be != "":
await ctx.defer()
if await moderate(api_key=api_key, text=pretend_to_be):
await ctx.respond(
"This has been flagged as inappropriate by OpenAI, please choose another name",
ephemeral=True,
)
return
if pretend_to_be == "":
pretend_to_be = ""
curs_data.execute(
"UPDATE data SET pretend_enabled = 0 WHERE guild_id = ?",
(ctx_to_guid(ctx),),
)
con_data.commit()
await ctx.respond("Pretend mode disabled", ephemeral=True, delete_after=5)
await ctx.guild.me.edit(nick=None)
return
else:
curs_data.execute(
"UPDATE data SET pretend_enabled = 1 WHERE guild_id = ?",
(ctx_to_guid(ctx),),
)
con_data.commit()
await ctx.respond("Pretend mode enabled", ephemeral=True, delete_after=5)
# change the bots name on the server wit ctx.guild.me.edit(nick=pretend_to_be)
await ctx.guild.me.edit(nick=pretend_to_be)
curs_data.execute(
"UPDATE data SET pretend_to_be = ? WHERE guild_id = ?",
(pretend_to_be, ctx_to_guid(ctx)),
)
con_data.commit()
# if the usename is longer than 32 characters, shorten it
if len(pretend_to_be) > 31:
pretend_to_be = pretend_to_be[:32]
await ctx.guild.me.edit(nick=pretend_to_be)
return
@discord.slash_command(name="enable_tts", description="Enable TTS when chatting")
@default_permissions(administrator=True)
async def enable_tts(self, ctx: discord.ApplicationContext):
# get the guild id
guild_id = ctx_to_guid(ctx)
# connect to the database
# update the tts value in the database
curs_data.execute("UPDATE data SET tts = 1 WHERE guild_id = ?", (guild_id,))
con_data.commit()
# send a message
await ctx.respond("TTS has been enabled", ephemeral=True)
@discord.slash_command(name="disable_tts", description="Disable TTS when chatting")
@default_permissions(administrator=True)
async def disable_tts(self, ctx: discord.ApplicationContext):
# get the guild id
guild_id = ctx_to_guid(ctx)
# connect to the database
# update the tts value in the database
curs_data.execute("UPDATE data SET tts = 0 WHERE guild_id = ?", (guild_id,))
con_data.commit()
# send a message
await ctx.respond("TTS has been disabled", ephemeral=True)
# autocompletition
async def autocomplete(ctx: discord.AutocompleteContext):
return [model for model in models if model.startswith(ctx.value)]
@discord.slash_command(name="model", description="Change the model used by the bot")
@discord.option(
name="model",
description="The model you want to use. Leave blank to use the davinci model",
required=False,
autocomplete=autocomplete,
)
@default_permissions(administrator=True)
async def model(self, ctx: discord.ApplicationContext, model: str = "davinci"):
try:
curs_data.execute("SELECT * FROM model WHERE guild_id = ?", (ctx_to_guid(ctx),))
data = curs_data.fetchone()[1]
except:
data = None
if data is None:
curs_data.execute("INSERT INTO model VALUES (?, ?)", (ctx_to_guid(ctx), model))
else:
curs_data.execute(
"UPDATE model SET model_name = ? WHERE guild_id = ?",
(model, ctx_to_guid(ctx)),
)
con_data.commit()
await ctx.respond("Model changed !", ephemeral=True)
async def images_recognition_autocomplete(ctx: discord.AutocompleteContext):
return [state for state in images_recognition if state.startswith(ctx.value)]
@discord.slash_command(
name="images", description="Enable or disable images recognition"
)
@discord.option(
name="enable_disable",
description="Enable or disable images recognition",
autocomplete=images_recognition_autocomplete,
)
@default_permissions(administrator=True)
async def images(self, ctx: discord.ApplicationContext, enable_disable: str):
try:
curs_data.execute(
"SELECT * FROM images WHERE guild_id = ?", (ctx_to_guid(ctx),)
)
data = curs_data.fetchone()
except:
data = None
if enable_disable == "enable":
enable_disable = 1
elif enable_disable == "disable":
enable_disable = 0
if data is None:
curs_data.execute(
"INSERT INTO images VALUES (?, ?, ?)", (ctx_to_guid(ctx), 0, enable_disable)
)
else:
curs_data.execute(
"UPDATE images SET is_enabled = ? WHERE guild_id = ?",
(enable_disable, ctx_to_guid(ctx)),
)
con_data.commit()
await ctx.respond(
"Images recognition has been "
+ ("enabled" if enable_disable == 1 else "disabled"),
ephemeral=True,
)

320
src/cogs/setup.py Normal file
View File

@@ -0,0 +1,320 @@
import discord
from discord import default_permissions, guild_only
from discord.ext import commands
from src.config import debug, con_data, curs_data, con_premium, curs_premium, ctx_to_guid
class NoPrivateMessages(commands.CheckFailure):
pass
def dms_only():
async def predicate(ctx):
if ctx.guild is not None:
raise NoPrivateMessages('Hey no private messages!')
return True
return commands.check(predicate)
class Setup(discord.Cog):
def __init__(self, bot: discord.Bot):
super().__init__()
self.bot = bot
@discord.slash_command(name="setup", description="Setup the bot")
@discord.option(name="channel_id", description="The channel id", required=True)
@discord.option(name="api_key", description="The api key", required=True)
@default_permissions(administrator=True)
@guild_only()
async def setup(
self,
ctx: discord.ApplicationContext,
channel: discord.TextChannel,
api_key: str,
):
if channel is None:
await ctx.respond("Invalid channel id", ephemeral=True)
return
try:
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,))
data = curs_data.fetchone()
if data[3] == None:
data = None
except:
data = None
if data != None:
curs_data.execute(
"UPDATE data SET channel_id = ?, api_key = ? WHERE guild_id = ?",
(channel.id, api_key, ctx.guild.id),
)
# c.execute("UPDATE data SET is_active = ?, max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?", (False, 64, 0.9, 0.0, 0.0, 5, ctx.guild.id))
con_data.commit()
await ctx.respond(
"The channel id and the api key have been updated", ephemeral=True
)
else:
curs_data.execute(
"INSERT INTO data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
ctx.guild.id,
channel.id,
api_key,
False,
64,
0.9,
0.0,
0.0,
0,
5,
"",
False,
"",
False,
),
)
con_data.commit()
await ctx.respond(
"The channel id and the api key have been added", ephemeral=True
)
@discord.slash_command(name="setup_dms", description="Setup the bot in dms")
@discord.option(name="api_key", description="The api key", required=True)
@default_permissions(administrator=True)
@dms_only()
async def setup_dms(
self,
ctx: discord.ApplicationContext,
api_key: str,
):
channel = ctx.channel
if channel is None:
await ctx.respond("Invalid channel id", ephemeral=True)
return
try:
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.user.id,))
data = curs_data.fetchone()
if data[3] == None:
data = None
except:
data = None
if data != None:
curs_data.execute(
"UPDATE data SET channel_id = ?, api_key = ? WHERE guild_id = ?",
(channel.id, api_key, ctx.user.id),
)
con_data.commit()
await ctx.respond(
"The channel id and the api key have been updated", ephemeral=True
)
else:
curs_data.execute(
"INSERT INTO data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(
ctx.user.id,
channel.id,
api_key,
False,
64,
0.9,
0.0,
0.0,
0,
5,
"",
False,
"",
False,
),
)
con_data.commit()
await ctx.respond(
"The api key has been added", ephemeral=True
)
@discord.slash_command(
name="delete", description="Delete the information about this server"
)
@default_permissions(administrator=True)
async def delete(self, ctx: discord.ApplicationContext):
# check if the guild is in the database
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),))
if curs_data.fetchone() is None:
await ctx.respond("This server is not setup", ephemeral=True)
return
# delete the guild from the database, except the guild id and the uses_count_today
curs_data.execute(
"UPDATE data SET api_key = ?, channel_id = ?, is_active = ?, max_tokens = ?, temperature = ?, frequency_penalty = ?, presence_penalty = ?, prompt_size = ? WHERE guild_id = ?",
(None, None, False, 50, 0.9, 0.0, 0.0, 0, ctx_to_guid(ctx)),
)
con_data.commit()
await ctx.respond("Deleted", ephemeral=True)
# create a command called "enable" that only admins can use
@discord.slash_command(name="enable", description="Enable the bot")
@default_permissions(administrator=True)
async def enable(self, ctx: discord.ApplicationContext):
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),))
if curs_data.fetchone() is None:
await ctx.respond("This server is not setup", ephemeral=True)
return
# enable the guild
curs_data.execute(
"UPDATE data SET is_active = ? WHERE guild_id = ?", (True, ctx_to_guid(ctx))
)
con_data.commit()
await ctx.respond("Enabled", ephemeral=True)
# create a command called "disable" that only admins can use
@discord.slash_command(name="disable", description="Disable the bot")
@default_permissions(administrator=True)
async def disable(self, ctx: discord.ApplicationContext):
# check if the guild is in the database
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx_to_guid(ctx),))
if curs_data.fetchone() is None:
await ctx.respond("This server is not setup", ephemeral=True)
return
# disable the guild
curs_data.execute(
"UPDATE data SET is_active = ? WHERE guild_id = ?", (False, ctx_to_guid(ctx))
)
con_data.commit()
await ctx.respond("Disabled", ephemeral=True)
# create a command calles "add channel" that can only be used in premium servers
@discord.slash_command(
name="add_channel",
description="Add a channel to the list of channels. Premium only.",
)
@discord.option(
name="channel",
description="The channel to add",
type=discord.TextChannel,
required=False,
)
@default_permissions(administrator=True)
@guild_only()
async def add_channel(
self, ctx: discord.ApplicationContext, channel: discord.TextChannel = None
):
# check if the guild is in the database
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,))
if curs_data.fetchone() is None:
await ctx.respond("This server is not setup", ephemeral=True)
return
# check if the guild is premium
try:
con_premium.execute(
"SELECT premium FROM data WHERE guild_id = ?", (ctx.guild.id,)
)
premium = con_premium.fetchone()[0]
except:
premium = 0
if not premium:
await ctx.respond("This server is not premium", ephemeral=True)
return
if channel is None:
channel = ctx.channel
# check if the channel is already in the list
curs_data.execute(
"SELECT channel_id FROM data WHERE guild_id = ?", (ctx.guild.id,)
)
if str(channel.id) == curs_data.fetchone()[0]:
await ctx.respond(
"This channel is already set as the main channel", ephemeral=True
)
return
con_premium.execute(
"SELECT * FROM channels WHERE guild_id = ?", (ctx.guild.id,)
)
guild_channels = con_premium.fetchone()
if guild_channels is None:
# if the channel is not in the list, add it
con_premium.execute(
"INSERT INTO channels VALUES (?, ?, ?, ?, ?, ?)",
(ctx.guild.id, channel.id, None, None, None, None),
)
con_premium.commit()
await ctx.respond(f"Added channel **{channel.name}**", ephemeral=True)
return
channels = guild_channels[1:]
if str(channel.id) in channels:
await ctx.respond("This channel is already added", ephemeral=True)
return
for i in range(5):
if channels[i] == None:
con_premium.execute(
f"UPDATE channels SET channel{i} = ? WHERE guild_id = ?",
(channel.id, ctx.guild.id),
)
con_premium.commit()
await ctx.respond(f"Added channel **{channel.name}**", ephemeral=True)
return
await ctx.respond("You can only add 5 channels", ephemeral=True)
# create a command called "remove channel" that can only be used in premium servers
@discord.slash_command(
name="remove_channel",
description="Remove a channel from the list of channels. Premium only.",
)
@discord.option(
name="channel",
description="The channel to remove",
type=discord.TextChannel,
required=False,
)
@default_permissions(administrator=True)
@guild_only()
async def remove_channel(
self, ctx: discord.ApplicationContext, channel: discord.TextChannel = None
):
# check if the guild is in the database
curs_data.execute("SELECT * FROM data WHERE guild_id = ?", (ctx.guild.id,))
if curs_data.fetchone() is None:
await ctx.respond("This server is not setup", ephemeral=True)
return
# check if the guild is premium
try:
con_premium.execute(
"SELECT premium FROM data WHERE guild_id = ?", (ctx.guild.id,)
)
premium = con_premium.fetchone()[0]
except:
premium = 0
if not premium:
await ctx.respond("This server is not premium", ephemeral=True)
return
if channel is None:
channel = ctx.channel
# check if the channel is in the list
con_premium.execute(
"SELECT * FROM channels WHERE guild_id = ?", (ctx.guild.id,)
)
guild_channels = con_premium.fetchone()
curs_data.execute(
"SELECT channel_id FROM data WHERE guild_id = ?", (ctx.guild.id,)
)
if str(channel.id) == curs_data.fetchone()[0]:
await ctx.respond(
"This channel is set as the main channel and therefore cannot be removed. Type /setup to change the main channel.",
ephemeral=True,
)
return
if guild_channels is None:
await ctx.respond(
"This channel was not added. Nothing changed", ephemeral=True
)
return
channels = guild_channels[1:]
if str(channel.id) not in channels:
await ctx.respond(
"This channel was not added. Nothing changed", ephemeral=True
)
return
# remove the channel from the list
for i in range(5):
if channels[i] == str(channel.id):
con_premium.execute(
f"UPDATE channels SET channel{i} = ? WHERE guild_id = ?",
(None, ctx.guild.id),
)
con_premium.commit()
await ctx.respond(f"Removed channel **{channel.name}**", ephemeral=True)
return